From e50304e3f81c003c4ab4222c1612be087e241087 Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 18 Jun 2018 18:20:27 -0600 Subject: [PATCH] organized and commented shader implementation code --- examples/shaders/waterreflection/main.go | 53 +++---- pixelgl/attr.go | 102 +++++++++++++ pixelgl/canvas.go | 66 ++++---- pixelgl/shader.go | 182 ++++++----------------- pixelgl/window.go | 5 + 5 files changed, 205 insertions(+), 203 deletions(-) create mode 100644 pixelgl/attr.go diff --git a/examples/shaders/waterreflection/main.go b/examples/shaders/waterreflection/main.go index b1762d2..ff41a5a 100644 --- a/examples/shaders/waterreflection/main.go +++ b/examples/shaders/waterreflection/main.go @@ -11,7 +11,6 @@ import ( _ "image/png" "github.com/faiface/pixel" - "github.com/faiface/pixel/imdraw" "github.com/faiface/pixel/pixelgl" ) @@ -49,44 +48,26 @@ func run() { sprite := pixel.NewSprite(pic, pic.Bounds()) - bounds := pic.Bounds() - sc := pixelgl.NewCanvas(pic.Bounds()) - sc2 := pixelgl.NewCanvas(pic.Bounds()) - sc2.SetFragmentShader(reflectionShader) - sc2.BindUniform("u_time", &utime) - sc2.RecompileShader() - imd := imdraw.New(nil) - imd.Color = colornames.Blueviolet - imd.EndShape = imdraw.RoundEndShape + wc := win.GetCanvas() + wc.SetFragmentShader(reflectionShader) + wc.BindUniform("u_time", &utime) + wc.UpdateShader() curpos := pixel.V(sc.Bounds().Center().X, -25) tgtpos := pixel.V(sc.Bounds().Center().X, 316) last := start for !win.Closed() { - dt := time.Since(last).Seconds() last = time.Now() curpos = pixel.Lerp(curpos, tgtpos, 1-math.Pow(1.0/16, dt*0.5)) sc.Clear(colornames.Black) - sc2.Clear(colornames.Black) - win.Clear(colornames.Black) - imd.Clear() - imd.Push(pixel.V(0, 0), pixel.V(bounds.Max.X, 0)) - imd.Push(pixel.V(bounds.Max.X, 0), pixel.V(bounds.Max.X, bounds.Max.Y)) - imd.Push(pixel.V(bounds.Max.X, bounds.Max.Y), pixel.V(0, bounds.Max.Y)) - imd.Push(pixel.V(0, bounds.Max.Y), pixel.V(0, 0)) - imd.Line(3) utime = float32(time.Since(start).Seconds()) sprite.Draw(sc, pixel.IM.Moved(curpos)) - sc.Draw(sc2, pixel.IM.Moved(sc2.Bounds().Center())) - - imd.Draw(sc2) - sc2.Draw(win, pixel.IM.Moved(win.Bounds().Center())) - + sc.Draw(wc, pixel.IM.Moved(wc.Bounds().Center())) win.Update() } } @@ -99,25 +80,25 @@ var reflectionShader = ` #version 330 core in vec4 Color; -in vec2 TexCoords; +in vec2 texcoords; in float Intensity; -out vec4 color; +out vec4 fragColor; -uniform vec4 colorMask; -uniform vec4 texBounds; -uniform sampler2D tex; +uniform vec4 u_colormask; +uniform vec4 u_texbounds; +uniform sampler2D u_texture; uniform float u_time; void main() { if (Intensity == 0) { - color = colorMask * Color; + fragColor = u_colormask * Color; } else { - color = vec4(0, 0, 0, 0); - color += (1 - Intensity) * Color; - vec2 t = (TexCoords - texBounds.xy) / texBounds.zw; - color += Intensity * Color * texture(tex, t); - color *= colorMask; + fragColor = vec4(0, 0, 0, 0); + fragColor += (1 - Intensity) * Color; + vec2 t = (texcoords - u_texbounds.xy) / u_texbounds.zw; + fragColor += Intensity * Color * texture(u_texture, t); + fragColor *= u_colormask; vec2 uv = t; vec3 overlayColor = vec3(0.1,0.1,1); float sepoffset = 0.005*cos(u_time*3.0); @@ -126,7 +107,7 @@ void main() { { float xoffset = 0.005*cos(u_time*3.0+200.0*t.y); float yoffset = ((0.3 - t.y)/0.3) * 0.05*(1.0+cos(u_time*3.0+50.0*t.y)); - color = texture(tex, vec2(t.x+xoffset,t.y+yoffset)); + fragColor = texture(u_texture, vec2(t.x+xoffset,t.y+yoffset)); } } } diff --git a/pixelgl/attr.go b/pixelgl/attr.go new file mode 100644 index 0000000..c540bd1 --- /dev/null +++ b/pixelgl/attr.go @@ -0,0 +1,102 @@ +package pixelgl + +import "github.com/go-gl/mathgl/mgl32" + +// AttrType is the attribute's identifier +type AttrType int + +// List of all possible attribute types. +const ( + Int AttrType = iota + Float + Vec2 + Vec3 + Vec4 + Mat2 + Mat23 + Mat24 + Mat3 + Mat32 + Mat34 + Mat4 + Mat42 + Mat43 + Intp // pointers + Floatp + Vec2p + Vec3p + Vec4p + Mat2p + Mat23p + Mat24p + Mat3p + Mat32p + Mat34p + Mat4p + Mat42p + Mat43p +) + +// Returns the type identifier for any (supported) variable type +func getAttrType(v interface{}) AttrType { + switch v.(type) { + case int32: + return Int + case float32: + return Float + case mgl32.Vec2: + return Vec2 + case mgl32.Vec3: + return Vec3 + case mgl32.Vec4: + return Vec4 + case mgl32.Mat2: + return Mat2 + case mgl32.Mat2x3: + return Mat23 + case mgl32.Mat2x4: + return Mat24 + case mgl32.Mat3: + return Mat3 + case mgl32.Mat3x2: + return Mat32 + case mgl32.Mat3x4: + return Mat34 + case mgl32.Mat4: + return Mat4 + case mgl32.Mat4x2: + return Mat42 + case mgl32.Mat4x3: + return Mat43 + case *mgl32.Vec2: + return Vec2p + case *mgl32.Vec3: + return Vec3p + case *mgl32.Vec4: + return Vec4p + case *mgl32.Mat2: + return Mat2p + case *mgl32.Mat2x3: + return Mat23p + case *mgl32.Mat2x4: + return Mat24p + case *mgl32.Mat3: + return Mat3p + case *mgl32.Mat3x2: + return Mat32p + case *mgl32.Mat3x4: + return Mat34p + case *mgl32.Mat4: + return Mat4p + case *mgl32.Mat4x2: + return Mat42p + case *mgl32.Mat4x3: + return Mat43p + case *int32: + return Intp + case *float32: + return Floatp + default: + panic("invalid AttrType") + } +} diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go index b89fc9e..41cd130 100644 --- a/pixelgl/canvas.go +++ b/pixelgl/canvas.go @@ -39,10 +39,29 @@ func NewCanvas(bounds pixel.Rect) *Canvas { baseShader(c) c.SetBounds(bounds) - c.shader.compile() + c.shader.update() return c } +// BindUniform will add a uniform with any supported underlying variable +// if the uniform already exists, including defaults, they will be reassigned +// to the new value +func (c *Canvas) BindUniform(Name string, Value interface{}) { + c.shader.AddUniform(Name, Value) +} + +// UpdateShader needs to be called after any changes to the underlying GLShader +// are made (ie, BindUniform(), SetFragmentShader()...) +func (c *Canvas) UpdateShader() { + c.shader.update() +} + +// SetFragmentShader allows you to define a new fragment shader on the underlying +// GLShader. fs is the GLSL source, not a filename +func (c *Canvas) SetFragmentShader(fs string) { + c.shader.fs = fs +} + // MakeTriangles creates a specialized copy of the supplied Triangles that draws onto this Canvas. // // TrianglesPosition, TrianglesColor and TrianglesPicture are supported. @@ -170,6 +189,25 @@ func setBlendFunc(cmp pixel.ComposeMethod) { } } +// updates all uniform values for gl to consume +func (c *Canvas) setUniforms(texbounds pixel.Rect) { + mat := c.mat + col := c.col + c.shader.uniformDefaults.transform = mat + c.shader.uniformDefaults.colormask = col + dstBounds := c.Bounds() + c.shader.uniformDefaults.bounds = mgl32.Vec4{ + float32(dstBounds.Min.X), + float32(dstBounds.Min.Y), + float32(dstBounds.W()), + float32(dstBounds.H()), + } + + for loc, u := range c.shader.uniforms { + c.shader.s.SetUniformAttr(loc, u.Value) + } +} + // Clear fills the whole Canvas with a single color. func (c *Canvas) Clear(color color.Color) { c.gf.Dirty() @@ -353,29 +391,3 @@ var defaultCanvasVertexFormat = glhf.AttrFormat{ canvasTexCoords: {Name: "texCoords", Type: glhf.Vec2}, canvasIntensity: {Name: "intensity", Type: glhf.Float}, } - -const ( - canvasTransform int = iota - canvasColorMask - canvasBounds - canvasTexBounds -) - -var canvasUniformFormat = glhf.AttrFormat{ - canvasTransform: {Name: "transform", Type: glhf.Mat3}, - canvasColorMask: {Name: "colorMask", Type: glhf.Vec4}, - canvasBounds: {Name: "bounds", Type: glhf.Vec4}, - canvasTexBounds: {Name: "texBounds", Type: glhf.Vec4}, -} - -func (c *Canvas) BindUniform(Name string, Value interface{}) { - c.shader.AddUniform(Name, Value) -} - -func (c *Canvas) RecompileShader() { - c.shader.compile() -} - -func (c *Canvas) SetFragmentShader(fs string) { - c.shader.fs = fs -} diff --git a/pixelgl/shader.go b/pixelgl/shader.go index f90a154..15f74bc 100644 --- a/pixelgl/shader.go +++ b/pixelgl/shader.go @@ -3,11 +3,13 @@ package pixelgl import ( "github.com/faiface/glhf" "github.com/faiface/mainthread" - "github.com/faiface/pixel" "github.com/go-gl/mathgl/mgl32" "github.com/pkg/errors" ) +// GLShader is a type to assist with managing a canvas's underlying +// shader configuration. This allows for customization of shaders on +// a per canvas basis. type ( GLShader struct { s *glhf.Shader @@ -31,7 +33,8 @@ type ( } ) -func (gs *GLShader) compile() { +// reinitialize GLShader data and recompile the underlying gl shader object +func (gs *GLShader) update() { gs.uf = nil for _, u := range gs.uniforms { gs.uf = append(gs.uf, glhf.Attr{ @@ -55,7 +58,9 @@ func (gs *GLShader) compile() { gs.s = shader } -func (gs *GLShader) GetUniform(Name string) int { + +// gets the uniform index from GLShader +func (gs *GLShader) getUniform(Name string) int { for i, u := range gs.uniforms { if u.Name == Name { return i @@ -63,9 +68,17 @@ func (gs *GLShader) GetUniform(Name string) int { } return -1 } + +// AddUniform appends a custom uniform name and value to the shader +// +// To add a time uniform for example: +// +// utime := float32(time.Since(starttime)).Seconds()) +// mycanvas.shader.AddUniform("u_time", &utime) +// func (gs *GLShader) AddUniform(Name string, Value interface{}) { - Type := getUniformType(Value) - if loc := gs.GetUniform(Name); loc > -1 { + Type := getAttrType(Value) + if loc := gs.getUniform(Name); loc > -1 { gs.uniforms[loc].Name = Name gs.uniforms[loc].Type = Type gs.uniforms[loc].Value = Value @@ -78,24 +91,9 @@ func (gs *GLShader) AddUniform(Name string, Value interface{}) { }) } -func (c *Canvas) setUniforms(texbounds pixel.Rect) { - mat := c.mat - col := c.col - c.shader.uniformDefaults.transform = mat - c.shader.uniformDefaults.colormask = col - dstBounds := c.Bounds() - c.shader.uniformDefaults.bounds = mgl32.Vec4{ - float32(dstBounds.Min.X), - float32(dstBounds.Min.Y), - float32(dstBounds.W()), - float32(dstBounds.H()), - } - - for loc, u := range c.shader.uniforms { - c.shader.s.SetUniformAttr(loc, u.Value) - } -} - +// Sets up a base shader with everything needed for a pixel +// canvas to render correctly. The defaults can be overridden +// by simply using AddUniform() func baseShader(c *Canvas) { gs := &GLShader{ vf: defaultCanvasVertexFormat, @@ -103,109 +101,13 @@ func baseShader(c *Canvas) { fs: baseCanvasFragmentShader, } - gs.AddUniform("transform", &gs.uniformDefaults.transform) - gs.AddUniform("colorMask", &gs.uniformDefaults.colormask) - gs.AddUniform("bounds", &gs.uniformDefaults.bounds) - gs.AddUniform("texBounds", &gs.uniformDefaults.texbounds) + gs.AddUniform("u_transform", &gs.uniformDefaults.transform) + gs.AddUniform("u_colormask", &gs.uniformDefaults.colormask) + gs.AddUniform("u_bounds", &gs.uniformDefaults.bounds) + gs.AddUniform("u_texbounds", &gs.uniformDefaults.texbounds) c.shader = gs } -func getUniformType(v interface{}) AttrType { - switch v.(type) { - case int32: - return Int - case float32: - return Float - case mgl32.Vec2: - return Vec2 - case mgl32.Vec3: - return Vec3 - case mgl32.Vec4: - return Vec4 - case mgl32.Mat2: - return Mat2 - case mgl32.Mat2x3: - return Mat23 - case mgl32.Mat2x4: - return Mat24 - case mgl32.Mat3: - return Mat3 - case mgl32.Mat3x2: - return Mat32 - case mgl32.Mat3x4: - return Mat34 - case mgl32.Mat4: - return Mat4 - case mgl32.Mat4x2: - return Mat42 - case mgl32.Mat4x3: - return Mat43 - case *mgl32.Vec2: - return Vec2p - case *mgl32.Vec3: - return Vec3p - case *mgl32.Vec4: - return Vec4p - case *mgl32.Mat2: - return Mat2p - case *mgl32.Mat2x3: - return Mat23p - case *mgl32.Mat2x4: - return Mat24p - case *mgl32.Mat3: - return Mat3p - case *mgl32.Mat3x2: - return Mat32p - case *mgl32.Mat3x4: - return Mat34p - case *mgl32.Mat4: - return Mat4p - case *mgl32.Mat4x2: - return Mat42p - case *mgl32.Mat4x3: - return Mat43p - case *int32: - return Intp - case *float32: - return Floatp - default: - panic("invalid AttrType") - } -} - -type AttrType int - -// List of all possible attribute types. -const ( - Int AttrType = iota - Float - Vec2 - Vec3 - Vec4 - Mat2 - Mat23 - Mat24 - Mat3 - Mat32 - Mat34 - Mat4 - Mat42 - Mat43 - Intp - Floatp - Vec2p - Vec3p - Vec4p - Mat2p - Mat23p - Mat24p - Mat3p - Mat32p - Mat34p - Mat4p - Mat42p - Mat43p -) var defaultCanvasVertexShader = ` #version 330 core @@ -216,18 +118,18 @@ in vec2 texCoords; in float intensity; out vec4 Color; -out vec2 TexCoords; +out vec2 texcoords; out float Intensity; -uniform mat3 transform; -uniform vec4 bounds; +uniform mat3 u_transform; +uniform vec4 u_bounds; void main() { - vec2 transPos = (transform * vec3(position, 1.0)).xy; - vec2 normPos = (transPos - bounds.xy) / bounds.zw * 2 - vec2(1, 1); + vec2 transPos = (u_transform * vec3(position, 1.0)).xy; + vec2 normPos = (transPos - u_bounds.xy) / u_bounds.zw * 2 - vec2(1, 1); gl_Position = vec4(normPos, 0.0, 1.0); Color = color; - TexCoords = texCoords; + texcoords = texCoords; Intensity = intensity; } ` @@ -236,24 +138,24 @@ var baseCanvasFragmentShader = ` #version 330 core in vec4 Color; -in vec2 TexCoords; +in vec2 texcoords; in float Intensity; -out vec4 color; +out vec4 fragColor; -uniform vec4 colorMask; -uniform vec4 texBounds; -uniform sampler2D tex; +uniform vec4 u_colormask; +uniform vec4 u_texbounds; +uniform sampler2D u_texture; void main() { if (Intensity == 0) { - color = colorMask * Color; + fragColor = u_colormask * Color; } else { - color = vec4(0, 0, 0, 0); - color += (1 - Intensity) * Color; - vec2 t = (TexCoords - texBounds.xy) / texBounds.zw; - color += Intensity * Color * texture(tex, t); - color *= colorMask; + fragColor = vec4(0, 0, 0, 0); + fragColor += (1 - Intensity) * Color; + vec2 t = (texcoords - u_texbounds.xy) / u_texbounds.zw; + fragColor += Intensity * Color * texture(u_texture, t); + fragColor *= u_colormask; } } ` diff --git a/pixelgl/window.go b/pixelgl/window.go index ddc5426..de7bb0d 100644 --- a/pixelgl/window.go +++ b/pixelgl/window.go @@ -424,3 +424,8 @@ func (w *Window) Clear(c color.Color) { func (w *Window) Color(at pixel.Vec) pixel.RGBA { return w.canvas.Color(at) } + +// GetCanvas returns the window's underlying Canvas +func (w *Window) GetCanvas() *Canvas { + return w.canvas +}