shader.go (3357B)
1 package tofu 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 8 "github.com/go-gl/gl/v3.3-core/gl" 9 ) 10 11 // CompileShader compiles shaders of type xtype using the source file specified by path. 12 // It returns the resulting shader ID. 13 func compileShader(path string, xtype uint32) (shaderID uint32, err error) { 14 src, err := os.ReadFile(path) 15 if err != nil { 16 return 0, fmt.Errorf("readfile %s: %v", path, err) 17 } 18 shaderID = gl.CreateShader(xtype) 19 if shaderID == 0 { 20 return 0, fmt.Errorf("can't create shader") 21 } 22 cstrs, free := gl.Strs(string(src) + "\x00") 23 defer free() 24 gl.ShaderSource(shaderID, 1, cstrs, nil) 25 gl.CompileShader(shaderID) 26 27 var success int32 28 gl.GetShaderiv(shaderID, gl.COMPILE_STATUS, &success) 29 if success == gl.FALSE { 30 var logLength int32 31 gl.GetShaderiv(shaderID, gl.INFO_LOG_LENGTH, &logLength) 32 l := gl.Str(strings.Repeat("\x00", int(logLength))) 33 gl.GetShaderInfoLog(shaderID, logLength, nil, l) 34 gl.DeleteShader(shaderID) 35 return 0, fmt.Errorf("%v", gl.GoStr(l)) 36 } 37 return shaderID, nil 38 } 39 40 // LinkShaders links vertex shader vsID and fragment shader fsID and returns the 41 // resulting shader program. 42 func linkShaders(vsID, fsID uint32) (shaderID uint32, err error) { 43 shaderID = gl.CreateProgram() 44 gl.AttachShader(shaderID, vsID) 45 gl.AttachShader(shaderID, fsID) 46 gl.LinkProgram(shaderID) 47 48 var success int32 49 gl.GetProgramiv(shaderID, gl.LINK_STATUS, &success) 50 if success == gl.FALSE { 51 var logLength int32 52 gl.GetProgramiv(shaderID, gl.INFO_LOG_LENGTH, &logLength) 53 l := gl.Str(strings.Repeat("\x00", int(logLength))) 54 gl.GetProgramInfoLog(shaderID, logLength, nil, l) 55 return 0, fmt.Errorf("%v", gl.GoStr(l)) 56 } 57 return shaderID, nil 58 } 59 60 type Program struct { 61 id uint32 62 } 63 64 // NewProgram creates a shader program from a vertex shader source file 65 // specified by vpath and a fragment shader source file 66 // specified by fpath, and returns the resulting Program object. 67 func NewProgram(vpath, fpath string) (*Program, error) { 68 vid, err := compileShader(vpath, gl.VERTEX_SHADER) 69 if err != nil { 70 return nil, fmt.Errorf("compile vertex shader: %v", err) 71 } 72 fid, err := compileShader(fpath, gl.FRAGMENT_SHADER) 73 if err != nil { 74 return nil, fmt.Errorf("compile fragment shader: %v", err) 75 } 76 pid, err := linkShaders(vid, fid) 77 if err != nil { 78 return nil, fmt.Errorf("link shaders: %v", err) 79 } 80 gl.DeleteShader(vid) 81 gl.DeleteShader(fid) 82 return &Program{id: pid}, nil 83 } 84 85 // Use activates the shader. 86 func (p *Program) Use() { 87 gl.UseProgram(p.id) 88 } 89 90 // SetFloat32 set the value of the uniform name. 91 // TODO: Should I check error conditions? 92 func (p *Program) SetFloat32(name string, val float32) error { 93 l := gl.GetUniformLocation(p.id, gl.Str(name + "\x00")) 94 if l == -1 { 95 return fmt.Errorf("uniform %s not found", name) 96 } 97 gl.Uniform1f(l, val) 98 return nil 99 } 100 101 func (p *Program) SetMat4(name string, val Mat4) error { 102 l := gl.GetUniformLocation(p.id, gl.Str(name + "\x00")) 103 if l == -1 { 104 return fmt.Errorf("uniform %s not found", name) 105 } 106 gl.UniformMatrix4fv(l, 1, false, &val[0]) 107 return nil 108 } 109 110 func (p *Program) SetTexture(t *Texture, name string) error { 111 p.Use() 112 gl.ActiveTexture(t.unit) 113 gl.BindTexture(gl.TEXTURE_2D, t.id) 114 l := gl.GetUniformLocation(p.id, gl.Str(name + "\x00")) 115 if l == -1 { 116 return fmt.Errorf("no such uniform: %s", name) 117 } 118 gl.Uniform1i(l, int32(t.unit - gl.TEXTURE0)) 119 return nil 120 }