From c4b1dd1b3d4c371aad5b10731e7fa93cbddd0a3a Mon Sep 17 00:00:00 2001 From: bloeys Date: Mon, 15 Apr 2024 05:09:07 +0400 Subject: [PATCH] Geometry shader support+omnidirectional depth map shader+improvements --- assets/textures.go | 3 +- buffers/buf_usage.go | 4 +- main.go | 30 ++++++++- materials/material.go | 24 +++---- ...th-map.glsl => directional-depth-map.glsl} | 0 res/shaders/omnidirectional-depth-map.glsl | 62 +++++++++++++++++++ shaders/shader_program.go | 46 +++++++++----- shaders/shader_types.go | 29 +++++++-- shaders/shaders.go | 62 +++++++++++-------- 9 files changed, 201 insertions(+), 59 deletions(-) rename res/shaders/{depth-map.glsl => directional-depth-map.glsl} (100%) create mode 100755 res/shaders/omnidirectional-depth-map.glsl diff --git a/assets/textures.go b/assets/textures.go index ad3431e..713895f 100755 --- a/assets/textures.go +++ b/assets/textures.go @@ -19,7 +19,8 @@ import ( type ColorFormat int const ( - ColorFormat_RGBA8 ColorFormat = iota + ColorFormat_Unknown ColorFormat = iota + ColorFormat_RGBA8 ) type Texture struct { diff --git a/buffers/buf_usage.go b/buffers/buf_usage.go index c541804..e25a7f2 100755 --- a/buffers/buf_usage.go +++ b/buffers/buf_usage.go @@ -10,8 +10,10 @@ import ( type BufUsage int const ( + BufUsage_Unknown BufUsage = iota + //Buffer is set only once and used many times - BufUsage_Static BufUsage = iota + BufUsage_Static //Buffer is changed a lot and used many times BufUsage_Dynamic //Buffer is set only once and used by the GPU at most a few times diff --git a/main.go b/main.go index c37ea75..e68bc4a 100755 --- a/main.go +++ b/main.go @@ -30,6 +30,9 @@ import ( - Directional lights ✅ - Point lights ✅ - Spotlights ✅ + - Directional light shadows ✅ + - Point light shadows + - Spotlight shadows - HDR - Cascaded shadow mapping - Skeletal animations @@ -85,6 +88,28 @@ type PointLight struct { Quadratic float32 } +var ( + pointLightNear float32 = 1 + pointLightFar float32 = 25 +) + +func (p *PointLight) GetProjViewMats(shadowMapWidth, shadowMapHeight float32) [6]gglm.Mat4 { + + aspect := float32(shadowMapWidth) / float32(shadowMapHeight) + projMat := gglm.Perspective(90*gglm.Deg2Rad, aspect, pointLightNear, pointLightFar) + + projViewMats := [6]gglm.Mat4{ + *projMat.Clone().Mul(&gglm.LookAtRH(&p.Pos, gglm.NewVec3(1, 0, 0).Add(&p.Pos), gglm.NewVec3(0, -1, 0)).Mat4), + *projMat.Clone().Mul(&gglm.LookAtRH(&p.Pos, gglm.NewVec3(-1, 0, 0).Add(&p.Pos), gglm.NewVec3(0, -1, 0)).Mat4), + *projMat.Clone().Mul(&gglm.LookAtRH(&p.Pos, gglm.NewVec3(0, 1, 0).Add(&p.Pos), gglm.NewVec3(0, 0, 1)).Mat4), + *projMat.Clone().Mul(&gglm.LookAtRH(&p.Pos, gglm.NewVec3(0, -1, 0).Add(&p.Pos), gglm.NewVec3(0, 0, -1)).Mat4), + *projMat.Clone().Mul(&gglm.LookAtRH(&p.Pos, gglm.NewVec3(0, 0, 1).Add(&p.Pos), gglm.NewVec3(0, -1, 0)).Mat4), + *projMat.Clone().Mul(&gglm.LookAtRH(&p.Pos, gglm.NewVec3(0, 0, -1).Add(&p.Pos), gglm.NewVec3(0, -1, 0)).Mat4), + } + + return projViewMats +} + type SpotLight struct { Pos gglm.Vec3 Dir gglm.Vec3 @@ -141,6 +166,7 @@ var ( palleteMat *materials.Material skyboxMat *materials.Material dirLightDepthMapMat *materials.Material + omnidirDepthMapMat *materials.Material debugDepthMat *materials.Material cubeMesh *meshes.Mesh @@ -437,7 +463,9 @@ func (g *Game) Init() { debugDepthMat = materials.NewMaterial("Debug depth mat", "./res/shaders/debug-depth.glsl") - dirLightDepthMapMat = materials.NewMaterial("Depth Map mat", "./res/shaders/depth-map.glsl") + dirLightDepthMapMat = materials.NewMaterial("Directional Depth Map mat", "./res/shaders/directional-depth-map.glsl") + + omnidirDepthMapMat = materials.NewMaterial("Omnidirectional Depth Map mat", "./res/shaders/omnidirectional-depth-map.glsl") skyboxMat = materials.NewMaterial("Skybox mat", "./res/shaders/skybox.glsl") skyboxMat.CubemapTex = skyboxCmap.TexID diff --git a/materials/material.go b/materials/material.go index 9b12235..1d476e9 100755 --- a/materials/material.go +++ b/materials/material.go @@ -44,7 +44,7 @@ type Material struct { func (m *Material) Bind() { - gl.UseProgram(m.ShaderProg.ID) + m.ShaderProg.Bind() if m.DiffuseTex != 0 { gl.ActiveTexture(uint32(gl.TEXTURE0 + TextureSlot_Diffuse)) @@ -88,7 +88,7 @@ func (m *Material) GetAttribLoc(attribName string) int32 { return loc } - loc = gl.GetAttribLocation(m.ShaderProg.ID, gl.Str(attribName+"\x00")) + loc = gl.GetAttribLocation(m.ShaderProg.Id, gl.Str(attribName+"\x00")) assert.T(loc != -1, "Attribute '"+attribName+"' doesn't exist on material "+m.Name) m.AttribLocs[attribName] = loc return loc @@ -101,7 +101,7 @@ func (m *Material) GetUnifLoc(uniformName string) int32 { return loc } - loc = gl.GetUniformLocation(m.ShaderProg.ID, gl.Str(uniformName+"\x00")) + loc = gl.GetUniformLocation(m.ShaderProg.Id, gl.Str(uniformName+"\x00")) assert.T(loc != -1, "Uniform '"+uniformName+"' doesn't exist on material "+m.Name) m.UnifLocs[uniformName] = loc return loc @@ -116,39 +116,39 @@ func (m *Material) DisableAttribute(attribName string) { } func (m *Material) SetUnifInt32(uniformName string, val int32) { - gl.ProgramUniform1i(m.ShaderProg.ID, m.GetUnifLoc(uniformName), val) + gl.ProgramUniform1i(m.ShaderProg.Id, m.GetUnifLoc(uniformName), val) } func (m *Material) SetUnifFloat32(uniformName string, val float32) { - gl.ProgramUniform1f(m.ShaderProg.ID, m.GetUnifLoc(uniformName), val) + gl.ProgramUniform1f(m.ShaderProg.Id, m.GetUnifLoc(uniformName), val) } func (m *Material) SetUnifVec2(uniformName string, vec2 *gglm.Vec2) { - gl.ProgramUniform2fv(m.ShaderProg.ID, m.GetUnifLoc(uniformName), 1, &vec2.Data[0]) + gl.ProgramUniform2fv(m.ShaderProg.Id, m.GetUnifLoc(uniformName), 1, &vec2.Data[0]) } func (m *Material) SetUnifVec3(uniformName string, vec3 *gglm.Vec3) { - gl.ProgramUniform3fv(m.ShaderProg.ID, m.GetUnifLoc(uniformName), 1, &vec3.Data[0]) + gl.ProgramUniform3fv(m.ShaderProg.Id, m.GetUnifLoc(uniformName), 1, &vec3.Data[0]) } func (m *Material) SetUnifVec4(uniformName string, vec4 *gglm.Vec4) { - gl.ProgramUniform4fv(m.ShaderProg.ID, m.GetUnifLoc(uniformName), 1, &vec4.Data[0]) + gl.ProgramUniform4fv(m.ShaderProg.Id, m.GetUnifLoc(uniformName), 1, &vec4.Data[0]) } func (m *Material) SetUnifMat2(uniformName string, mat2 *gglm.Mat2) { - gl.ProgramUniformMatrix2fv(m.ShaderProg.ID, m.GetUnifLoc(uniformName), 1, false, &mat2.Data[0][0]) + gl.ProgramUniformMatrix2fv(m.ShaderProg.Id, m.GetUnifLoc(uniformName), 1, false, &mat2.Data[0][0]) } func (m *Material) SetUnifMat3(uniformName string, mat3 *gglm.Mat3) { - gl.ProgramUniformMatrix3fv(m.ShaderProg.ID, m.GetUnifLoc(uniformName), 1, false, &mat3.Data[0][0]) + gl.ProgramUniformMatrix3fv(m.ShaderProg.Id, m.GetUnifLoc(uniformName), 1, false, &mat3.Data[0][0]) } func (m *Material) SetUnifMat4(uniformName string, mat4 *gglm.Mat4) { - gl.ProgramUniformMatrix4fv(m.ShaderProg.ID, m.GetUnifLoc(uniformName), 1, false, &mat4.Data[0][0]) + gl.ProgramUniformMatrix4fv(m.ShaderProg.Id, m.GetUnifLoc(uniformName), 1, false, &mat4.Data[0][0]) } func (m *Material) Delete() { - gl.DeleteProgram(m.ShaderProg.ID) + gl.DeleteProgram(m.ShaderProg.Id) } func NewMaterial(matName, shaderPath string) *Material { diff --git a/res/shaders/depth-map.glsl b/res/shaders/directional-depth-map.glsl similarity index 100% rename from res/shaders/depth-map.glsl rename to res/shaders/directional-depth-map.glsl diff --git a/res/shaders/omnidirectional-depth-map.glsl b/res/shaders/omnidirectional-depth-map.glsl new file mode 100755 index 0000000..70d99d6 --- /dev/null +++ b/res/shaders/omnidirectional-depth-map.glsl @@ -0,0 +1,62 @@ +//shader:vertex +#version 410 + +layout(location=0) in vec3 vertPosIn; + +uniform mat4 modelMat; + +void main() +{ + gl_Position = modelMat * vec4(vertPosIn, 1); +} + +//shader:geometry +#version 410 + +layout (triangles) in; + +// Cubemap means 6 faces, and the +// input 3 triangle vertices are drawn once per face, so 6*3=18 +layout (triangle_strip, max_vertices=18) out; + +uniform mat4 cubemapProjViewMats[6]; + +out vec4 FragPos; + +void main() +{ + for(int face = 0; face < 6; ++face) + { + // Built in variable that specifies which cubemap face we are rendering to + // and only works when a cubemap is attached to the active fbo + gl_Layer = face; + + // Transform each triangle vertex + for(int i = 0; i < 3; ++i) + { + FragPos = gl_in[i].gl_Position; + gl_Position = cubemapProjViewMats[face] * FragPos; + EmitVertex(); + } + EndPrimitive(); + } +} + +//shader:fragment +#version 410 + +in vec4 FragPos; + +uniform vec3 lightPos; +uniform float farPlane; + +void main() +{ + // Get distance between fragment and light source + float lightDistance = length(FragPos.xyz - lightPos); + + // Map to [0, 1] by dividing by far plane and use it as our depth + lightDistance = lightDistance / farPlane; + + gl_FragDepth = lightDistance; +} diff --git a/shaders/shader_program.go b/shaders/shader_program.go index cea6fbb..aab4ab8 100755 --- a/shaders/shader_program.go +++ b/shaders/shader_program.go @@ -6,32 +6,48 @@ import ( ) type ShaderProgram struct { - ID uint32 - VertShaderID uint32 - FragShaderID uint32 + Id uint32 + VertShaderId uint32 + FragShaderId uint32 + GeomShaderId uint32 } func (sp *ShaderProgram) AttachShader(shader Shader) { - gl.AttachShader(sp.ID, shader.ID) - switch shader.ShaderType { - case VertexShaderType: - sp.VertShaderID = shader.ID - case FragmentShaderType: - sp.FragShaderID = shader.ID + gl.AttachShader(sp.Id, shader.Id) + switch shader.Type { + case ShaderType_Vertex: + sp.VertShaderId = shader.Id + case ShaderType_Fragment: + sp.FragShaderId = shader.Id + case ShaderType_Geometry: + sp.GeomShaderId = shader.Id default: - logging.ErrLog.Println("Unknown shader type ", shader.ShaderType, " for ID ", shader.ID) + logging.ErrLog.Fatalf("Unknown shader type '%d' for shader id '%d'\n", shader.Type, shader.Id) } } func (sp *ShaderProgram) Link() { - gl.LinkProgram(sp.ID) + gl.LinkProgram(sp.Id) - if sp.VertShaderID != 0 { - gl.DeleteShader(sp.VertShaderID) + if sp.VertShaderId != 0 { + gl.DeleteShader(sp.VertShaderId) } - if sp.FragShaderID != 0 { - gl.DeleteShader(sp.FragShaderID) + + if sp.FragShaderId != 0 { + gl.DeleteShader(sp.FragShaderId) + } + + if sp.GeomShaderId != 0 { + gl.DeleteShader(sp.GeomShaderId) } } + +func (s *ShaderProgram) Bind() { + gl.UseProgram(s.Id) +} + +func (s *ShaderProgram) UnBind() { + gl.UseProgram(0) +} diff --git a/shaders/shader_types.go b/shaders/shader_types.go index 72c1f4e..33d19ac 100755 --- a/shaders/shader_types.go +++ b/shaders/shader_types.go @@ -1,10 +1,31 @@ package shaders -import "github.com/go-gl/gl/v4.1-core/gl" +import ( + "github.com/bloeys/nmage/logging" + "github.com/go-gl/gl/v4.1-core/gl" +) -type ShaderType int +type ShaderType int32 + +func (s ShaderType) ToGl() uint32 { + + switch s { + case ShaderType_Vertex: + return gl.VERTEX_SHADER + case ShaderType_Fragment: + return gl.FRAGMENT_SHADER + case ShaderType_Geometry: + return gl.GEOMETRY_SHADER + + default: + logging.ErrLog.Fatalf("Unknown shader type '%d'\n", s) + return 0 + } +} const ( - VertexShaderType ShaderType = gl.VERTEX_SHADER - FragmentShaderType ShaderType = gl.FRAGMENT_SHADER + ShaderType_Unknown ShaderType = iota + ShaderType_Vertex + ShaderType_Fragment + ShaderType_Geometry ) diff --git a/shaders/shaders.go b/shaders/shaders.go index 8d40f05..95a77be 100755 --- a/shaders/shaders.go +++ b/shaders/shaders.go @@ -3,6 +3,7 @@ package shaders import ( "bytes" "errors" + "fmt" "os" "strings" @@ -11,12 +12,13 @@ import ( ) type Shader struct { - ID uint32 - ShaderType ShaderType + Id uint32 + Type ShaderType } -func (s Shader) Delete() { - gl.DeleteShader(s.ID) +func (s *Shader) Delete() { + gl.DeleteShader(s.Id) + s.Id = 0 } func NewShaderProgram() (ShaderProgram, error) { @@ -26,7 +28,7 @@ func NewShaderProgram() (ShaderProgram, error) { return ShaderProgram{}, errors.New("failed to create shader program") } - return ShaderProgram{ID: id}, nil + return ShaderProgram{Id: id}, nil } func LoadAndCompileCombinedShader(shaderPath string) (ShaderProgram, error) { @@ -43,8 +45,8 @@ func LoadAndCompileCombinedShader(shaderPath string) (ShaderProgram, error) { func LoadAndCompileCombinedShaderSrc(shaderSrc []byte) (ShaderProgram, error) { shaderSources := bytes.Split(shaderSrc, []byte("//shader:")) - if len(shaderSources) == 1 { - return ShaderProgram{}, errors.New("failed to read combined shader. Did not find '//shader:vertex' or '//shader:fragment'") + if len(shaderSources) < 2 { + return ShaderProgram{}, errors.New("failed to read combined shader. The minimum shader types to have are '//shader:vertex' and '//shader:fragment'") } shdrProg, err := NewShaderProgram() @@ -65,12 +67,15 @@ func LoadAndCompileCombinedShaderSrc(shaderSrc []byte) (ShaderProgram, error) { var shdrType ShaderType if bytes.HasPrefix(src, []byte("vertex")) { src = src[6:] - shdrType = VertexShaderType + shdrType = ShaderType_Vertex } else if bytes.HasPrefix(src, []byte("fragment")) { src = src[8:] - shdrType = FragmentShaderType + shdrType = ShaderType_Fragment + } else if bytes.HasPrefix(src, []byte("geometry")) { + src = src[8:] + shdrType = ShaderType_Geometry } else { - return ShaderProgram{}, errors.New("unknown shader type. Must be '//shader:vertex' or '//shader:fragment'") + return ShaderProgram{}, errors.New("unknown shader type. Must be '//shader:vertex' or '//shader:fragment' or '//shader:geometry'") } shdr, err := CompileShaderOfType(src, shdrType) @@ -83,7 +88,15 @@ func LoadAndCompileCombinedShaderSrc(shaderSrc []byte) (ShaderProgram, error) { } if loadedShdrCount == 0 { - return ShaderProgram{}, errors.New("no valid shaders found. Please put '//shader:vertex' or '//shader:fragment' before your shaders") + return ShaderProgram{}, errors.New("no valid shaders found. Please put '//shader:vertex' or '//shader:fragment' or '//shader:geometry' before your shaders") + } + + if shdrProg.VertShaderId == 0 { + return ShaderProgram{}, errors.New("no valid vertex shader found. Please put '//shader:vertex' before your vertex shader") + } + + if shdrProg.FragShaderId == 0 { + return ShaderProgram{}, errors.New("no valid fragment shader found. Please put '//shader:fragment' before your vertex shader") } shdrProg.Link() @@ -92,41 +105,40 @@ func LoadAndCompileCombinedShaderSrc(shaderSrc []byte) (ShaderProgram, error) { func CompileShaderOfType(shaderSource []byte, shaderType ShaderType) (Shader, error) { - shaderID := gl.CreateShader(uint32(shaderType)) - if shaderID == 0 { - logging.ErrLog.Println("Failed to create shader.") - return Shader{}, errors.New("failed to create shader") + shaderId := gl.CreateShader(shaderType.ToGl()) + if shaderId == 0 { + return Shader{}, fmt.Errorf("failed to create OpenGl shader. OpenGl Error=%d", gl.GetError()) } //Load shader source and compile shaderCStr, shaderFree := gl.Strs(string(shaderSource) + "\x00") defer shaderFree() - gl.ShaderSource(shaderID, 1, shaderCStr, nil) + gl.ShaderSource(shaderId, 1, shaderCStr, nil) - gl.CompileShader(shaderID) - if err := getShaderCompileErrors(shaderID); err != nil { - gl.DeleteShader(shaderID) + gl.CompileShader(shaderId) + if err := getShaderCompileErrors(shaderId); err != nil { + gl.DeleteShader(shaderId) return Shader{}, err } - return Shader{ID: shaderID, ShaderType: shaderType}, nil + return Shader{Id: shaderId, Type: shaderType}, nil } -func getShaderCompileErrors(shaderID uint32) error { +func getShaderCompileErrors(shaderId uint32) error { var compiledSuccessfully int32 - gl.GetShaderiv(shaderID, gl.COMPILE_STATUS, &compiledSuccessfully) + gl.GetShaderiv(shaderId, gl.COMPILE_STATUS, &compiledSuccessfully) if compiledSuccessfully == gl.TRUE { return nil } var logLength int32 - gl.GetShaderiv(shaderID, gl.INFO_LOG_LENGTH, &logLength) + gl.GetShaderiv(shaderId, gl.INFO_LOG_LENGTH, &logLength) log := gl.Str(strings.Repeat("\x00", int(logLength))) - gl.GetShaderInfoLog(shaderID, logLength, nil, log) + gl.GetShaderInfoLog(shaderId, logLength, nil, log) errMsg := gl.GoStr(log) - logging.ErrLog.Println("Compilation of shader with id ", shaderID, " failed. Err: ", errMsg) + logging.ErrLog.Println("Compilation of shader with id ", shaderId, " failed. Err: ", errMsg) return errors.New(errMsg) }