organized and commented shader implementation code

This commit is contained in:
Brandon 2018-06-18 18:20:27 -06:00
parent e0c26f1caa
commit e50304e3f8
5 changed files with 205 additions and 203 deletions

View File

@ -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));
}
}
}

102
pixelgl/attr.go Normal file
View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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;
}
}
`

View File

@ -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
}