From 9c7a01b8b8dcfe5d01dfd90f0ef1e04f4c8ace6e Mon Sep 17 00:00:00 2001 From: Thomas Friedel Date: Thu, 8 Feb 2018 13:00:16 +0100 Subject: [PATCH] prepared radial gradients, but they don't work yet --- canvas.go | 65 +++++++++++++++++++++++++++-- gradients.go | 73 +++++++++++++++++++++----------- made_shaders.go | 109 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 217 insertions(+), 30 deletions(-) diff --git a/canvas.go b/canvas.go index db4b0ce..0bc42e0 100644 --- a/canvas.go +++ b/canvas.go @@ -37,6 +37,7 @@ type drawState struct { transform lm.Mat3x3 fill struct { color glColor + radialGradient *RadialGradient linearGradient *LinearGradient } stroke struct { @@ -126,6 +127,7 @@ var ( sr *solidShader tr *textureShader lgr *linearGradientShader + rgr *radialGradientShader glChan = make(chan func()) ) @@ -161,6 +163,15 @@ func LoadGL(glimpl GL) (err error) { return } + rgr, err = loadRadialGradientShader() + if err != nil { + return + } + err = glError() + if err != nil { + return + } + gli.GenBuffers(1, &buf) err = glError() if err != nil { @@ -212,7 +223,6 @@ uniform sampler2D image; void main() { gl_FragColor = texture2D(image, v_texCoord); }` - var linearGradientVS = ` attribute vec2 vertex; uniform vec2 canvasSize; @@ -229,10 +239,34 @@ precision mediump float; varying vec2 v_cp; uniform sampler1D gradient; uniform vec2 from, dir; -uniform float length; +uniform float len; void main() { vec2 v = v_cp - from; - float r = dot(v, dir) / length; + float r = dot(v, dir) / len; + r = clamp(r, 0.0, 1.0); + gl_FragColor = texture1D(gradient, r); +}` +var radialGradientVS = ` +attribute vec2 vertex; +uniform vec2 canvasSize; +varying vec2 v_cp; +void main() { + v_cp = vertex; + vec2 glp = vertex * 2.0 / canvasSize - 1.0; + gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0); +}` +var radialGradientFS = ` +#ifdef GL_ES +precision mediump float; +#endif +varying vec2 v_cp; +uniform sampler1D gradient; +uniform vec2 from, dir; +uniform float len; +void main() { + vec2 v0 = v_cp - from; + //vec2 v1 = v_cp - (from + dir); + float r = length(v0) / len; r = clamp(r, 0.0, 1.0); gl_FragColor = texture1D(gradient, r); }` @@ -249,11 +283,15 @@ func glError() error { func (cv *Canvas) SetFillStyle(value ...interface{}) { cv.state.fill.color = glColor{} cv.state.fill.linearGradient = nil + cv.state.fill.radialGradient = nil if len(value) == 1 { switch v := value[0].(type) { case *LinearGradient: cv.state.fill.linearGradient = v return + case *RadialGradient: + cv.state.fill.radialGradient = v + return } } c, ok := parseColor(value...) @@ -378,11 +416,30 @@ func (cv *Canvas) FillRect(x, y, w, h float32) { dir = dir.DivF(length) gli.Uniform2f(lgr.from, from[0], from[1]) gli.Uniform2f(lgr.dir, dir[0], dir[1]) - gli.Uniform1f(lgr.length, length) + gli.Uniform1f(lgr.len, length) gli.Uniform1i(lgr.gradient, 0) gli.EnableVertexAttribArray(lgr.vertex) gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4) gli.DisableVertexAttribArray(lgr.vertex) + } else if rg := cv.state.fill.radialGradient; rg != nil { + rg.load() + gli.UseProgram(rgr.id) + gli.VertexAttribPointer(rgr.vertex, 2, gl_FLOAT, false, 0, nil) + gli.ActiveTexture(gl_TEXTURE0) + gli.BindTexture(gl_TEXTURE_1D, rg.tex) + gli.Uniform2f(rgr.canvasSize, cv.fw, cv.fh) + from := cv.tf(rg.from) + to := cv.tf(rg.to) + dir := to.Sub(from) + length := dir.Len() + dir = dir.DivF(length) + gli.Uniform2f(rgr.from, from[0], from[1]) + gli.Uniform2f(rgr.dir, dir[0], dir[1]) + gli.Uniform1f(rgr.len, length) + gli.Uniform1i(rgr.gradient, 0) + gli.EnableVertexAttribArray(rgr.vertex) + gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4) + gli.DisableVertexAttribArray(rgr.vertex) } else { gli.UseProgram(sr.id) gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, nil) diff --git a/gradients.go b/gradients.go index 1b7aac1..e818571 100644 --- a/gradients.go +++ b/gradients.go @@ -8,6 +8,15 @@ import ( ) type LinearGradient struct { + gradient +} + +type RadialGradient struct { + gradient + radFrom, radTo float32 +} + +type gradient struct { from, to lm.Vec2 stops []gradientStop tex uint32 @@ -21,7 +30,7 @@ type gradientStop struct { } func NewLinearGradient(x0, y0, x1, y1 float32) *LinearGradient { - lg := &LinearGradient{from: lm.Vec2{x0, y0}, to: lm.Vec2{x1, y1}} + lg := &LinearGradient{gradient: gradient{from: lm.Vec2{x0, y0}, to: lm.Vec2{x1, y1}}} gli.GenTextures(1, &lg.tex) gli.ActiveTexture(gl_TEXTURE0) gli.BindTexture(gl_TEXTURE_1D, lg.tex) @@ -36,21 +45,37 @@ func NewLinearGradient(x0, y0, x1, y1 float32) *LinearGradient { return lg } -func (lg *LinearGradient) Delete() { - gli.DeleteTextures(1, &lg.tex) +func NewRadialGradient(x0, y0, r0, x1, y1, r1 float32) *RadialGradient { + rg := &RadialGradient{gradient: gradient{from: lm.Vec2{x0, y0}, to: lm.Vec2{x1, y1}}, radFrom: r0, radTo: r1} + gli.GenTextures(1, &rg.tex) + gli.ActiveTexture(gl_TEXTURE0) + gli.BindTexture(gl_TEXTURE_1D, rg.tex) + gli.TexParameteri(gl_TEXTURE_1D, gl_TEXTURE_MIN_FILTER, gl_LINEAR) + gli.TexParameteri(gl_TEXTURE_1D, gl_TEXTURE_MAG_FILTER, gl_LINEAR) + gli.TexParameteri(gl_TEXTURE_1D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE) + runtime.SetFinalizer(rg, func(rg *RadialGradient) { + glChan <- func() { + gli.DeleteTextures(1, &rg.tex) + } + }) + return rg } -func (lg *LinearGradient) load() { - if lg.loaded { +func (g *gradient) Delete() { + gli.DeleteTextures(1, &g.tex) +} + +func (g *gradient) load() { + if g.loaded { return } gli.ActiveTexture(gl_TEXTURE0) - gli.BindTexture(gl_TEXTURE_1D, lg.tex) + gli.BindTexture(gl_TEXTURE_1D, g.tex) var pixels [2048 * 4]byte pp := 0 for i := 0; i < 2048; i++ { - c := lg.colorAt(float32(i) / 2047) + c := g.colorAt(float32(i) / 2047) pixels[pp] = byte(fmath.Floor(c.r*255 + 0.5)) pixels[pp+1] = byte(fmath.Floor(c.g*255 + 0.5)) pixels[pp+2] = byte(fmath.Floor(c.b*255 + 0.5)) @@ -58,17 +83,17 @@ func (lg *LinearGradient) load() { pp += 4 } gli.TexImage1D(gl_TEXTURE_1D, 0, gl_RGBA, 2048, 0, gl_RGBA, gl_UNSIGNED_BYTE, gli.Ptr(&pixels[0])) - lg.loaded = true + g.loaded = true } -func (lg *LinearGradient) colorAt(pos float32) glColor { - if len(lg.stops) == 0 { +func (g *gradient) colorAt(pos float32) glColor { + if len(g.stops) == 0 { return glColor{} - } else if len(lg.stops) == 1 { - return lg.stops[0].color + } else if len(g.stops) == 1 { + return g.stops[0].color } beforeIdx, afterIdx := -1, -1 - for i, stop := range lg.stops { + for i, stop := range g.stops { if stop.pos > pos { afterIdx = i break @@ -76,11 +101,11 @@ func (lg *LinearGradient) colorAt(pos float32) glColor { beforeIdx = i } if beforeIdx == -1 { - return lg.stops[0].color + return g.stops[0].color } else if afterIdx == -1 { - return lg.stops[len(lg.stops)-1].color + return g.stops[len(g.stops)-1].color } - before, after := lg.stops[beforeIdx], lg.stops[afterIdx] + before, after := g.stops[beforeIdx], g.stops[afterIdx] p := (pos - before.pos) / (after.pos - before.pos) var c glColor c.r = (after.color.r-before.color.r)*p + before.color.r @@ -90,19 +115,19 @@ func (lg *LinearGradient) colorAt(pos float32) glColor { return c } -func (lg *LinearGradient) AddColorStop(pos float32, color ...interface{}) { +func (g *gradient) AddColorStop(pos float32, color ...interface{}) { c, _ := parseColor(color...) - insert := len(lg.stops) - for i, stop := range lg.stops { + insert := len(g.stops) + for i, stop := range g.stops { if stop.pos > pos { insert = i break } } - lg.stops = append(lg.stops, gradientStop{}) - if insert < len(lg.stops)-1 { - copy(lg.stops[insert+1:], lg.stops[insert:len(lg.stops)-1]) + g.stops = append(g.stops, gradientStop{}) + if insert < len(g.stops)-1 { + copy(g.stops[insert+1:], g.stops[insert:len(g.stops)-1]) } - lg.stops[insert] = gradientStop{pos: pos, color: c} - lg.loaded = false + g.stops[insert] = gradientStop{pos: pos, color: c} + g.loaded = false } diff --git a/made_shaders.go b/made_shaders.go index 3c1761d..f714a90 100755 --- a/made_shaders.go +++ b/made_shaders.go @@ -213,7 +213,7 @@ type linearGradientShader struct { gradient int32 from int32 dir int32 - length int32 + len int32 } func loadLinearGradientShader() (*linearGradientShader, error) { @@ -306,7 +306,112 @@ func loadLinearGradientShader() (*linearGradientShader, error) { result.gradient = gli.GetUniformLocation(program, gli.Str("gradient\x00")) result.from = gli.GetUniformLocation(program, gli.Str("from\x00")) result.dir = gli.GetUniformLocation(program, gli.Str("dir\x00")) - result.length = gli.GetUniformLocation(program, gli.Str("length\x00")) + result.len = gli.GetUniformLocation(program, gli.Str("len\x00")) + + return result, nil +} + +type radialGradientShader struct { + id uint32 + vertex uint32 + canvasSize int32 + gradient int32 + from int32 + dir int32 + len int32 +} + +func loadRadialGradientShader() (*radialGradientShader, error) { + var vs, fs, program uint32 + + { + csource, freeFunc := gli.Strs(radialGradientVS + "\x00") + defer freeFunc() + + vs = gli.CreateShader(gl_VERTEX_SHADER) + gli.ShaderSource(vs, 1, csource, nil) + gli.CompileShader(vs) + + var logLength int32 + gli.GetShaderiv(vs, gl_INFO_LOG_LENGTH, &logLength) + if logLength > 0 { + shLog := strings.Repeat("\x00", int(logLength+1)) + gli.GetShaderInfoLog(vs, logLength, nil, gli.Str(shLog)) + fmt.Printf("VERTEX_SHADER compilation log:\n\n%s\n", shLog) + } + + var status int32 + gli.GetShaderiv(vs, gl_COMPILE_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(vs) + return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error compiling shader part, glError: " + fmt.Sprint(glErr)) + } + } + + { + csource, freeFunc := gli.Strs(radialGradientFS + "\x00") + defer freeFunc() + + fs = gli.CreateShader(gl_FRAGMENT_SHADER) + gli.ShaderSource(fs, 1, csource, nil) + gli.CompileShader(fs) + + var logLength int32 + gli.GetShaderiv(fs, gl_INFO_LOG_LENGTH, &logLength) + if logLength > 0 { + shLog := strings.Repeat("\x00", int(logLength+1)) + gli.GetShaderInfoLog(fs, logLength, nil, gli.Str(shLog)) + fmt.Printf("FRAGMENT_SHADER compilation log:\n\n%s\n", shLog) + } + + var status int32 + gli.GetShaderiv(fs, gl_COMPILE_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(fs) + return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error compiling shader part, glError: " + fmt.Sprint(glErr)) + } + } + + { + program = gli.CreateProgram() + gli.AttachShader(program, vs) + gli.AttachShader(program, fs) + gli.LinkProgram(program) + + var logLength int32 + gli.GetProgramiv(program, gl_INFO_LOG_LENGTH, &logLength) + if logLength > 0 { + shLog := strings.Repeat("\x00", int(logLength+1)) + gli.GetProgramInfoLog(program, logLength, nil, gli.Str(shLog)) + fmt.Printf("Shader link log:\n\n%s\n", shLog) + } + + var status int32 + gli.GetProgramiv(program, gl_LINK_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(vs) + gli.DeleteShader(fs) + return nil, errors.New("error linking shader") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error linking shader, glError: " + fmt.Sprint(glErr)) + } + } + + result := &radialGradientShader{} + result.id = program + result.vertex = uint32(gli.GetAttribLocation(program, gli.Str("vertex\x00"))) + result.canvasSize = gli.GetUniformLocation(program, gli.Str("canvasSize\x00")) + result.gradient = gli.GetUniformLocation(program, gli.Str("gradient\x00")) + result.from = gli.GetUniformLocation(program, gli.Str("from\x00")) + result.dir = gli.GetUniformLocation(program, gli.Str("dir\x00")) + result.len = gli.GetUniformLocation(program, gli.Str("len\x00")) return result, nil }