From 9f4c34bcbbf1dc39eadf70fce030f69c857af77f Mon Sep 17 00:00:00 2001 From: bloeys Date: Sat, 9 Oct 2021 08:19:53 +0400 Subject: [PATCH] Shader handling --- main.go | 32 ++++++------- res/shaders/simple.frag.glsl | 5 ++ res/shaders/simple.vert.glsl | 5 ++ shaders/program.go | 89 ++++++++++++++++++++++++++++++++++++ shaders/shader.go | 65 ++++++++++++++++++++++++++ shaders/shader_type.go | 45 ++++++++++++++++++ 6 files changed, 225 insertions(+), 16 deletions(-) create mode 100755 res/shaders/simple.frag.glsl create mode 100755 res/shaders/simple.vert.glsl create mode 100755 shaders/program.go create mode 100755 shaders/shader.go create mode 100755 shaders/shader_type.go diff --git a/main.go b/main.go index 2405392..079704f 100755 --- a/main.go +++ b/main.go @@ -4,8 +4,9 @@ import ( "fmt" "github.com/bloeys/go-sdl-engine/input" + "github.com/bloeys/go-sdl-engine/shaders" "github.com/bloeys/go-sdl-engine/timing" - "github.com/go-gl/gl/v4.6-compatibility/gl" + "github.com/go-gl/gl/v4.6-core/gl" "github.com/veandco/go-sdl2/sdl" ) @@ -50,8 +51,7 @@ func main() { panicIfErr(err, "") //Run in compatiability (old and modern opengl) or modern (core) opengl only - // sdl.GLSetAttribute(sdl.GL_CONTEXT_PROFILE_MASK, sdl.GL_CONTEXT_PROFILE_CORE) - err = sdl.GLSetAttribute(sdl.GL_CONTEXT_PROFILE_MASK, sdl.GL_CONTEXT_PROFILE_COMPATIBILITY) + err = sdl.GLSetAttribute(sdl.GL_CONTEXT_PROFILE_MASK, sdl.GL_CONTEXT_PROFILE_CORE) panicIfErr(err, "") //Set wanted opengl version @@ -86,6 +86,7 @@ func main() { } initGL() + loadShaders() gameLoop() } @@ -99,6 +100,18 @@ func initGL() { gl.Viewport(0, 0, winWidth, winHeight) } +func loadShaders() { + + simpleProg := shaders.NewProgram("simple") + simpleVert, err := shaders.NewShaderFromFile("./res/shaders/simple.vert.glsl", shaders.Vertex) + panicIfErr(err, "Parsing vert shader failed") + simpleFrag, err := shaders.NewShaderFromFile("./res/shaders/simple.frag.glsl", shaders.Fragment) + panicIfErr(err, "Parsing frag shader failed") + + simpleProg.AttachShader(simpleVert) + simpleProg.AttachShader(simpleFrag) +} + func gameLoop() { for isRunning { @@ -138,24 +151,11 @@ func handleEvents() { } func update() { - println(input.AnyKeyDown(), ";", input.AnyMouseBtnDown()) } func draw() { //Clear screen and depth buffers gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) - - gl.Begin(gl.TRIANGLES) - - gl.Color3f(1, 0, 0) - gl.Vertex3f(0, 0.5, 0) - - gl.Color3f(1, 0, 0) - gl.Vertex3f(0.5, 0, 0) - - gl.Color3f(1, 0, 0) - gl.Vertex3f(-0.5, 0, 0) - gl.End() } func panicIfErr(err error, msg string) { diff --git a/res/shaders/simple.frag.glsl b/res/shaders/simple.frag.glsl new file mode 100755 index 0000000..4fcdd32 --- /dev/null +++ b/res/shaders/simple.frag.glsl @@ -0,0 +1,5 @@ +#version 400 + +void main() { + +} diff --git a/res/shaders/simple.vert.glsl b/res/shaders/simple.vert.glsl new file mode 100755 index 0000000..db688a9 --- /dev/null +++ b/res/shaders/simple.vert.glsl @@ -0,0 +1,5 @@ +#version 400 + +void main() { + +} \ No newline at end of file diff --git a/shaders/program.go b/shaders/program.go new file mode 100755 index 0000000..3bbe831 --- /dev/null +++ b/shaders/program.go @@ -0,0 +1,89 @@ +package shaders + +import ( + "fmt" + "log" + "strings" + + "github.com/go-gl/gl/v4.6-core/gl" +) + +type Program struct { + Name string + ID uint32 + Shaders []Shader +} + +func NewProgram(name string) Program { + + p := Program{Name: name} + p.Shaders = make([]Shader, 0) + + if p.ID = gl.CreateProgram(); p.ID == 0 { + log.Fatalln("Creating OpenGL program failed") + } + + return p +} + +//AttachShader adds the shader to list of shaders and attaches it in opengl +func (p *Program) AttachShader(s Shader) { + + p.Shaders = append(p.Shaders, s) + gl.AttachShader(p.ID, s.ID) +} + +//DetachShader removes the shader from the list of shaders and detaches it in opengl +func (p *Program) DetachShader(s Shader) { + + //To remove a shader we move the last shader to its place, then shrink the slice by one + for i := 0; i < len(p.Shaders); i++ { + + if p.Shaders[i].ID != s.ID { + continue + } + + gl.DetachShader(p.ID, s.ID) + + p.Shaders[i] = p.Shaders[len(p.Shaders)-1] + p.Shaders = p.Shaders[:len(p.Shaders)-1] + return + } +} + +func (p *Program) Link() error { + + gl.LinkProgram(p.ID) + return getProgramLinkError(*p) +} + +func getProgramLinkError(p Program) error { + + var linkSuccessful int32 + gl.GetProgramiv(p.ID, gl.LINK_STATUS, &linkSuccessful) + if linkSuccessful == gl.TRUE { + return nil + } + + //Get the log length and create a string big enough for it and fill it with NULL + var logLength int32 + gl.GetProgramiv(p.ID, gl.INFO_LOG_LENGTH, &logLength) + infoLog := gl.Str(strings.Repeat("\x00", int(logLength))) + + //Read the error log and return a go error + gl.GetProgramInfoLog(p.ID, logLength, nil, infoLog) + return fmt.Errorf("Program linking failed. Linking log: %s", gl.GoStr(infoLog)) +} + +func (p *Program) Use() { + gl.UseProgram(p.ID) +} + +//Delete deletes all shaders and then deletes the program +func (p *Program) Delete() { + for _, v := range p.Shaders { + v.Delete() + } + + gl.DeleteProgram(p.ID) +} diff --git a/shaders/shader.go b/shaders/shader.go new file mode 100755 index 0000000..3082c7e --- /dev/null +++ b/shaders/shader.go @@ -0,0 +1,65 @@ +package shaders + +import ( + "fmt" + "log" + "os" + "strings" + + "github.com/go-gl/gl/v4.6-core/gl" +) + +type Shader struct { + ID uint32 + Type ShaderType +} + +//NewShaderFromFile reads a shader from file, creates a new opengl shader and compiles it +func NewShaderFromFile(shaderFilePath string, st ShaderType) (Shader, error) { + + b, err := os.ReadFile(shaderFilePath) + if err != nil { + return Shader{}, err + } + + return NewShaderFromString(string(b), st) +} + +//NewShaderFromString creates a new opengl shader and compiles it +func NewShaderFromString(sourceString string, st ShaderType) (Shader, error) { + + glString, freeFunc := gl.Strs(sourceString + "\x00") + defer freeFunc() + + newShader := Shader{Type: st} + if newShader.ID = gl.CreateShader(st.GLType()); newShader.ID == 0 { + log.Fatalln("Creating shader failed. ShaderType:", st) + } + + gl.ShaderSource(newShader.ID, 1, glString, nil) + gl.CompileShader(newShader.ID) + + return newShader, getShaderCompileError(newShader) +} + +func getShaderCompileError(s Shader) error { + + var compileSuccessful int32 + gl.GetShaderiv(s.ID, gl.COMPILE_STATUS, &compileSuccessful) + if compileSuccessful == gl.TRUE { + return nil + } + + //Get the log length and create a string big enough for it and fill it with NULL + var logLength int32 + gl.GetShaderiv(s.ID, gl.INFO_LOG_LENGTH, &logLength) + infoLog := gl.Str(strings.Repeat("\x00", int(logLength))) + + //Read the error log and return a go error + gl.GetShaderInfoLog(s.ID, logLength, nil, infoLog) + return fmt.Errorf("Shader compilation failed. Compilation log: %s", gl.GoStr(infoLog)) +} + +func (s *Shader) Delete() { + gl.DeleteShader(s.ID) +} diff --git a/shaders/shader_type.go b/shaders/shader_type.go new file mode 100755 index 0000000..6ab4891 --- /dev/null +++ b/shaders/shader_type.go @@ -0,0 +1,45 @@ +package shaders + +import ( + "log" + + "github.com/go-gl/gl/v4.6-core/gl" +) + +type ShaderType int + +const ( + Unknown ShaderType = iota + Vertex + Fragment +) + +//GLType returns the GL shader type of this ShaderType +//Panics if not known +func (t ShaderType) GLType() uint32 { + + switch t { + case Vertex: + return gl.VERTEX_SHADER + case Fragment: + return gl.FRAGMENT_SHADER + } + + log.Panicf("Converting ShaderType->GL Shader Type failed. Unknown ShaderType of value: %v\n", t) + return 0 +} + +//FromGLShaderType returns the ShaderType of the passed GL shader type. +//Panics if not known +func (t ShaderType) FromGLShaderType(glShaderType int) ShaderType { + + switch glShaderType { + case gl.VERTEX_SHADER: + return Vertex + case gl.FRAGMENT_SHADER: + return Fragment + default: + log.Printf("Converting GL shader type->ShaderType failed. Unknown GL shader type of value: %v\n", glShaderType) + return Unknown + } +}