tofu

Making something with OpenGL in Go
Log | Files | Refs

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 }