220 lines
5.9 KiB
Go
220 lines
5.9 KiB
Go
package pixelgl
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
|
|
"github.com/go-gl/gl/v3.3-core/gl"
|
|
"github.com/go-gl/mathgl/mgl32"
|
|
)
|
|
|
|
// Shader is an OpenGL shader program.
|
|
type Shader struct {
|
|
program binder
|
|
vertexFmt AttrFormat
|
|
uniformFmt AttrFormat
|
|
uniforms map[string]int32
|
|
}
|
|
|
|
// NewShader creates a new shader program from the specified vertex shader and fragment shader
|
|
// sources.
|
|
//
|
|
// Note that vertexShader and fragmentShader parameters must contain the source code, they're
|
|
// not filenames.
|
|
func NewShader(vertexFmt, uniformFmt AttrFormat, vertexShader, fragmentShader string) (*Shader, error) {
|
|
shader := &Shader{
|
|
program: binder{
|
|
restoreLoc: gl.CURRENT_PROGRAM,
|
|
bindFunc: func(obj uint32) {
|
|
gl.UseProgram(obj)
|
|
},
|
|
},
|
|
vertexFmt: vertexFmt,
|
|
uniformFmt: uniformFmt,
|
|
uniforms: make(map[string]int32),
|
|
}
|
|
|
|
var vshader, fshader uint32
|
|
|
|
// vertex shader
|
|
{
|
|
vshader = gl.CreateShader(gl.VERTEX_SHADER)
|
|
src, free := gl.Strs(vertexShader)
|
|
defer free()
|
|
length := int32(len(vertexShader))
|
|
gl.ShaderSource(vshader, 1, src, &length)
|
|
gl.CompileShader(vshader)
|
|
|
|
var (
|
|
success int32
|
|
infoLog = make([]byte, 512)
|
|
)
|
|
gl.GetShaderiv(vshader, gl.COMPILE_STATUS, &success)
|
|
if success == 0 {
|
|
gl.GetShaderInfoLog(vshader, int32(len(infoLog)), nil, &infoLog[0])
|
|
return nil, fmt.Errorf("error compiling vertex shader: %s", string(infoLog))
|
|
}
|
|
|
|
defer gl.DeleteShader(vshader)
|
|
}
|
|
|
|
// fragment shader
|
|
{
|
|
fshader = gl.CreateShader(gl.FRAGMENT_SHADER)
|
|
src, free := gl.Strs(fragmentShader)
|
|
defer free()
|
|
length := int32(len(fragmentShader))
|
|
gl.ShaderSource(fshader, 1, src, &length)
|
|
gl.CompileShader(fshader)
|
|
|
|
var (
|
|
success int32
|
|
infoLog = make([]byte, 512)
|
|
)
|
|
gl.GetShaderiv(fshader, gl.COMPILE_STATUS, &success)
|
|
if success == 0 {
|
|
gl.GetShaderInfoLog(fshader, int32(len(infoLog)), nil, &infoLog[0])
|
|
return nil, fmt.Errorf("error compiling fragment shader: %s", string(infoLog))
|
|
}
|
|
|
|
defer gl.DeleteShader(fshader)
|
|
}
|
|
|
|
// shader program
|
|
{
|
|
shader.program.obj = gl.CreateProgram()
|
|
gl.AttachShader(shader.program.obj, vshader)
|
|
gl.AttachShader(shader.program.obj, fshader)
|
|
gl.LinkProgram(shader.program.obj)
|
|
|
|
var (
|
|
success int32
|
|
infoLog = make([]byte, 512)
|
|
)
|
|
gl.GetProgramiv(shader.program.obj, gl.LINK_STATUS, &success)
|
|
if success == 0 {
|
|
gl.GetProgramInfoLog(shader.program.obj, int32(len(infoLog)), nil, &infoLog[0])
|
|
return nil, fmt.Errorf("error linking shader program: %s", string(infoLog))
|
|
}
|
|
}
|
|
|
|
// uniforms
|
|
for name := range uniformFmt {
|
|
loc := gl.GetUniformLocation(shader.program.obj, gl.Str(name+"\x00"))
|
|
shader.uniforms[name] = loc
|
|
}
|
|
|
|
runtime.SetFinalizer(shader, (*Shader).delete)
|
|
|
|
return shader, nil
|
|
}
|
|
|
|
func (s *Shader) delete() {
|
|
DoNoBlock(func() {
|
|
gl.DeleteProgram(s.program.obj)
|
|
})
|
|
}
|
|
|
|
// ID returns an OpenGL identifier of a shader program.
|
|
func (s *Shader) ID() uint32 {
|
|
return s.program.obj
|
|
}
|
|
|
|
// VertexFormat returns the vertex attribute format of this shader. Do not change it.
|
|
func (s *Shader) VertexFormat() AttrFormat {
|
|
return s.vertexFmt
|
|
}
|
|
|
|
// UniformFormat returns the uniform attribute format of this shader. Do not change it.
|
|
func (s *Shader) UniformFormat() AttrFormat {
|
|
return s.uniformFmt
|
|
}
|
|
|
|
// SetUniformAttr sets the value of a uniform attribute of a shader.
|
|
//
|
|
// If the attribute does not exist, this method returns false.
|
|
//
|
|
// Supplied value must correspond to the type of the attribute. Correct types are these
|
|
// (right-hand is the type of the value):
|
|
// Attr{Type: Int}: int32
|
|
// Attr{Type: Float}: float32
|
|
// Attr{Type: Vec2}: mgl32.Vec2
|
|
// Attr{Type: Vec3}: mgl32.Vec3
|
|
// Attr{Type: Vec4}: mgl32.Vec4
|
|
// Attr{Type: Mat2}: mgl32.Mat2
|
|
// Attr{Type: Mat23}: mgl32.Mat2x3
|
|
// Attr{Type: Mat24}: mgl32.Mat2x4
|
|
// Attr{Type: Mat3}: mgl32.Mat3
|
|
// Attr{Type: Mat32}: mgl32.Mat3x2
|
|
// Attr{Type: Mat34}: mgl32.Mat3x4
|
|
// Attr{Type: Mat4}: mgl32.Mat4
|
|
// Attr{Type: Mat42}: mgl32.Mat4x2
|
|
// Attr{Type: Mat43}: mgl32.Mat4x3
|
|
// No other types are supported.
|
|
//
|
|
// The shader must be bound before calling this method.
|
|
func (s *Shader) SetUniformAttr(attr Attr, value interface{}) (ok bool) {
|
|
if !s.uniformFmt.Contains(attr) {
|
|
return false
|
|
}
|
|
|
|
switch attr.Type {
|
|
case Int:
|
|
value := value.(int32)
|
|
gl.Uniform1iv(s.uniforms[attr.Name], 1, &value)
|
|
case Float:
|
|
value := value.(float32)
|
|
gl.Uniform1fv(s.uniforms[attr.Name], 1, &value)
|
|
case Vec2:
|
|
value := value.(mgl32.Vec2)
|
|
gl.Uniform2fv(s.uniforms[attr.Name], 1, &value[0])
|
|
case Vec3:
|
|
value := value.(mgl32.Vec3)
|
|
gl.Uniform3fv(s.uniforms[attr.Name], 1, &value[0])
|
|
case Vec4:
|
|
value := value.(mgl32.Vec4)
|
|
gl.Uniform4fv(s.uniforms[attr.Name], 1, &value[0])
|
|
case Mat2:
|
|
value := value.(mgl32.Mat2)
|
|
gl.UniformMatrix2fv(s.uniforms[attr.Name], 1, false, &value[0])
|
|
case Mat23:
|
|
value := value.(mgl32.Mat2x3)
|
|
gl.UniformMatrix2x3fv(s.uniforms[attr.Name], 1, false, &value[0])
|
|
case Mat24:
|
|
value := value.(mgl32.Mat2x4)
|
|
gl.UniformMatrix2x4fv(s.uniforms[attr.Name], 1, false, &value[0])
|
|
case Mat3:
|
|
value := value.(mgl32.Mat3)
|
|
gl.UniformMatrix3fv(s.uniforms[attr.Name], 1, false, &value[0])
|
|
case Mat32:
|
|
value := value.(mgl32.Mat3x2)
|
|
gl.UniformMatrix3x2fv(s.uniforms[attr.Name], 1, false, &value[0])
|
|
case Mat34:
|
|
value := value.(mgl32.Mat3x4)
|
|
gl.UniformMatrix3x4fv(s.uniforms[attr.Name], 1, false, &value[0])
|
|
case Mat4:
|
|
value := value.(mgl32.Mat4)
|
|
gl.UniformMatrix4fv(s.uniforms[attr.Name], 1, false, &value[0])
|
|
case Mat42:
|
|
value := value.(mgl32.Mat4x2)
|
|
gl.UniformMatrix4x2fv(s.uniforms[attr.Name], 1, false, &value[0])
|
|
case Mat43:
|
|
value := value.(mgl32.Mat4x3)
|
|
gl.UniformMatrix4x3fv(s.uniforms[attr.Name], 1, false, &value[0])
|
|
default:
|
|
panic("set uniform attr: invalid attribute type")
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// Begin binds a shader program. This is necessary before using the shader.
|
|
func (s *Shader) Begin() {
|
|
s.program.bind()
|
|
}
|
|
|
|
// End unbinds a shader program and restores the previous one.
|
|
func (s *Shader) End() {
|
|
s.program.restore()
|
|
}
|