diff --git a/cmd/main.go b/cmd/main.go index 0b598c6..ed2ddbb 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -6,7 +6,9 @@ import ( "time" "edgaru089.ml/go/gl01/internal/game" + gio "edgaru089.ml/go/gl01/internal/io" _ "edgaru089.ml/go/gl01/internal/render/gpu_preference" + "edgaru089.ml/go/gl01/internal/util/itype" "github.com/go-gl/gl/all-core/gl" "github.com/go-gl/glfw/v3.3/glfw" ) @@ -50,6 +52,7 @@ func main() { game := game.NewGame() game.Init(win) + gio.ClearColor = itype.Vec3f{0.6, 0.8, 1.0} gl.ClearColor(0.6, 0.8, 1.0, 1) gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) win.SwapBuffers() diff --git a/internal/asset/shader.go b/internal/asset/shader.go index 8a6ed21..d457933 100644 --- a/internal/asset/shader.go +++ b/internal/asset/shader.go @@ -3,10 +3,6 @@ package asset import _ "embed" var ( - //go:embed shader/world/output.frag - WorldShaderFrag string - //go:embed shader/world/output.vert - WorldShaderVert string //go:embed shader/world/shadowmap.frag WorldShaderShadowmapFrag string @@ -18,10 +14,20 @@ var ( //go:embed shader/world/geometry.vert WorldShaderGeometryVert string + //go:embed shader/world/lighting.frag + WorldShaderLightingFrag string + //go:embed shader/world/lighting.vert + WorldShaderLightingVert string + //go:embed shader/world/water.frag WorldShaderWaterFrag string //go:embed shader/world/water.vert WorldShaderWaterVert string + + //go:embed shader/world/output.frag + WorldShaderOutputFrag string + //go:embed shader/world/output.vert + WorldShaderOutputVert string ) //go:embed shader/framewire.frag diff --git a/internal/asset/shader/world/geometry.frag b/internal/asset/shader/world/geometry.frag index 28f3bb5..4388bdf 100644 --- a/internal/asset/shader/world/geometry.frag +++ b/internal/asset/shader/world/geometry.frag @@ -4,6 +4,7 @@ uniform sampler2D tex; in vec3 fragPosWorld; in float fragPosLightspaceZ; +in float fragDepthNDC; in vec2 fragTexCoord; in vec3 fragNormal; in float fragLight; @@ -20,6 +21,7 @@ void main() { outputPosition.xyz = fragPosWorld; outputPosition.w = fragPosLightspaceZ; outputNormal.xyz = fragNormal; + outputNormal.w = fragDepthNDC; outputColor = texture(tex, fragTexCoord); outputColor = vec4(pow(outputColor.rgb, vec3(gamma)), outputColor.a); } diff --git a/internal/asset/shader/world/geometry.vert b/internal/asset/shader/world/geometry.vert index 353a062..c859b33 100644 --- a/internal/asset/shader/world/geometry.vert +++ b/internal/asset/shader/world/geometry.vert @@ -14,6 +14,7 @@ layout (location = 3) in float light; out vec3 fragPosWorld; out float fragPosLightspaceZ; +out float fragDepthNDC; out vec2 fragTexCoord; out vec3 fragNormal; out float fragLight; @@ -25,6 +26,7 @@ void main() { fragLight = light; gl_Position = projection * view * model * vec4(vert, 1); + fragDepthNDC = gl_Position.z / gl_Position.w; vec4 pos4 = model * vec4(vert, 1); fragPosWorld = pos4.xyz / pos4.w; diff --git a/internal/asset/shader/world/lighting.frag b/internal/asset/shader/world/lighting.frag new file mode 100644 index 0000000..c25a37e --- /dev/null +++ b/internal/asset/shader/world/lighting.frag @@ -0,0 +1,109 @@ +#version 330 + +uniform sampler2D shadowmap; +uniform mat4 lightspace; +uniform vec3 viewPos; +uniform vec3 sun; +uniform vec4 fogColor; + +// G-Buffers +uniform sampler2D gPos; +uniform sampler2D gNorm; +uniform sampler2D gColor; +// Fragment information from G-Buffers +vec4 fragPos; +vec4 fragPosLightspace; +float fragPosLightspaceZ; +vec3 fragNormal; +vec4 fragColor; + +in vec2 fragPosScreen; + +out vec4 outputColor; + + +const float gamma = 2.2; + +const float ambient = 0.3, specularStrength = 0.08, specularShininess = 8; +const float fogDensity = .00003; + + +float light; +vec4 texpixel, color; +void lightSun(); +float lightSunShadow(); +void lightPoint(int i); + + +void loadGBuffer() { + vec4 fragGNormal = texture(gNorm, fragPosScreen); + fragNormal = fragGNormal.xyz; + if (fragNormal == vec3(0.0f, 0.0f, 0.0f)) + discard; + + gl_FragDepth = fragGNormal.w * 0.5 + 0.5; + + vec4 fragGPos = texture(gPos, fragPosScreen); + fragPos = vec4(fragGPos.xyz + viewPos, 1.0f); + fragPosLightspaceZ = fragGPos.w; + fragColor = texture(gColor, fragPosScreen); + + fragPosLightspace = lightspace * fragPos; +} + + +void main() { + + loadGBuffer(); + + light = ambient; + + lightSun(); + + color += vec4(fragColor.rgb * light, 0.0f); + color.a = fragColor.a; + color.rgb = pow(color.rgb, vec3(1.0/gamma)); + + float z = gl_FragCoord.z / gl_FragCoord.w; + float fog = clamp(exp(-fogDensity * z * z), 0.2, 1); + + outputColor = mix(fogColor, color, fog); +} + +void lightSun() { + /* Diffuse */ + vec3 lightDir = sun; + float diffuse = max(dot(fragNormal, lightDir), 0.0f); + + /* Specular */ + vec3 viewDir = normalize(viewPos - fragPos.xyz); + vec3 reflectDir = reflect(-lightDir, fragNormal); + float specular = specularStrength * pow(max(dot(viewDir, reflectDir), 0.0), specularShininess); + + float shadow = lightSunShadow(); + light += diffuse * shadow; + color += vec4(vec3(specular), 0.0f) * shadow; +} + +float lightSunShadow() { + /* Shadow */ + float bias = max(0.0013 * (1.0 - dot(fragNormal, sun)), 0.0001); + vec3 projCoords = fragPosLightspace.xyz / fragPosLightspace.w; + projCoords = projCoords*0.5 + 0.5; + float closestDepth = texture(shadowmap, projCoords.xy).r; + //float currentDepth = projCoords.z; + float currentDepth = fragPosLightspaceZ; + float shadow = 0; + + if (currentDepth > 1.0f || currentDepth < 0.0f) + return 1.0f; + + //vec2 texelSize = clamp((currentDepth+bias-closestDepth)*100.0f, 0.05f, 1.5f) / textureSize(shadowmap, 0); + vec2 texelSize = 0.4 / textureSize(shadowmap, 0); + for (int x=-4; x<=4; ++x) + for (int y=-4; y<=4; ++y) { + float pcfDepth = texture(shadowmap, projCoords.xy + vec2(x,y)*texelSize).r; + shadow += currentDepth-bias < pcfDepth ? 1.0f : 0.0f; + } + return min(shadow/81.0f, 1.0f); +} diff --git a/internal/asset/shader/world/lighting.vert b/internal/asset/shader/world/lighting.vert new file mode 100644 index 0000000..fd8b422 --- /dev/null +++ b/internal/asset/shader/world/lighting.vert @@ -0,0 +1,12 @@ +#version 330 + +layout (location = 0) in vec2 vert; +layout (location = 1) in vec2 texCoord; + +out vec2 fragPosScreen; + +void main() { + gl_Position = vec4(vert, 0.0f, 1); + fragPosScreen = texCoord; +} + diff --git a/internal/asset/shader/world/output.frag b/internal/asset/shader/world/output.frag index a734769..2ce940e 100644 --- a/internal/asset/shader/world/output.frag +++ b/internal/asset/shader/world/output.frag @@ -1,106 +1,10 @@ #version 330 -uniform sampler2D shadowmap; -uniform mat4 lightspace; -uniform vec3 viewPos; -uniform vec3 sun; -uniform vec4 fogColor; - -// G-Buffers -uniform sampler2D gPos; -uniform sampler2D gNorm; -uniform sampler2D gColor; -// Fragment information from G-Buffers -vec4 fragPos; -vec4 fragPosLightspace; -float fragPosLightspaceZ; -vec3 fragNormal; -vec4 fragColor; +uniform sampler2D tex; in vec2 fragPosScreen; - out vec4 outputColor; - -const float gamma = 2.2; - -const float ambient = 0.3, specularStrength = 0.08, specularShininess = 8; -const float fogDensity = .00003; - - -float light; -vec4 texpixel, color; -void lightSun(); -float lightSunShadow(); -void lightPoint(int i); - - -void loadGBuffer() { - fragNormal = texture(gNorm, fragPosScreen).xyz; - if (fragNormal == vec3(0.0f, 0.0f, 0.0f)) - discard; - - vec4 fragGPos = texture(gPos, fragPosScreen); - fragPos = vec4(fragGPos.xyz + viewPos, 1.0f); - fragPosLightspaceZ = fragGPos.w; - fragColor = texture(gColor, fragPosScreen); - - fragPosLightspace = lightspace * fragPos; -} - - void main() { - - loadGBuffer(); - - light = ambient; - - lightSun(); - - color += vec4(fragColor.rgb * light, 0.0f); - color.a = fragColor.a; - color.rgb = pow(color.rgb, vec3(1.0/gamma)); - - float z = gl_FragCoord.z / gl_FragCoord.w; - float fog = clamp(exp(-fogDensity * z * z), 0.2, 1); - - outputColor = mix(fogColor, color, fog); -} - -void lightSun() { - /* Diffuse */ - vec3 lightDir = sun; - float diffuse = max(dot(fragNormal, lightDir), 0.0f); - - /* Specular */ - vec3 viewDir = normalize(viewPos - fragPos.xyz); - vec3 reflectDir = reflect(-lightDir, fragNormal); - float specular = specularStrength * pow(max(dot(viewDir, reflectDir), 0.0), specularShininess); - - float shadow = lightSunShadow(); - light += diffuse * shadow; - color += vec4(vec3(specular), 0.0f) * shadow; -} - -float lightSunShadow() { - /* Shadow */ - float bias = max(0.0013 * (1.0 - dot(fragNormal, sun)), 0.0001); - vec3 projCoords = fragPosLightspace.xyz / fragPosLightspace.w; - projCoords = projCoords*0.5 + 0.5; - float closestDepth = texture(shadowmap, projCoords.xy).r; - //float currentDepth = projCoords.z; - float currentDepth = fragPosLightspaceZ; - float shadow = 0; - - if (currentDepth > 1.0f || currentDepth < 0.0f) - return 1.0f; - - //vec2 texelSize = clamp((currentDepth+bias-closestDepth)*100.0f, 0.05f, 1.5f) / textureSize(shadowmap, 0); - vec2 texelSize = 0.4 / textureSize(shadowmap, 0); - for (int x=-4; x<=4; ++x) - for (int y=-4; y<=4; ++y) { - float pcfDepth = texture(shadowmap, projCoords.xy + vec2(x,y)*texelSize).r; - shadow += currentDepth-bias < pcfDepth ? 1.0f : 0.0f; - } - return min(shadow/81.0f, 1.0f); + outputColor = texture(tex, fragPosScreen); } diff --git a/internal/asset/shader/world/water.frag b/internal/asset/shader/world/water.frag index d8b76b6..47783d9 100644 --- a/internal/asset/shader/world/water.frag +++ b/internal/asset/shader/world/water.frag @@ -60,13 +60,14 @@ void lightSun() { vec3 viewDir = normalize(viewPos - fragPos.xyz); vec3 reflectDir = reflect(-lightDir, fragNormal); float specular = specularStrength * pow(max(dot(viewDir, reflectDir), 0.0), specularShininess); - if (specular > 1.0f) { - finalpha = min(finalpha + specular - 1.0f, 1.0f); - } float shadow = lightSunShadow(); light += diffuse * shadow; color += vec4(vec3(specular), 0.0f) * shadow; + + if (specular*shadow > 1.0f) { + finalpha = min(finalpha + specular - 1.0f, 1.0f); + } finalpha = min(finalpha + 0.1f * shadow, 1.0f); } diff --git a/internal/io/io.go b/internal/io/io.go index fd44407..ec6b4d9 100644 --- a/internal/io/io.go +++ b/internal/io/io.go @@ -5,6 +5,8 @@ import "edgaru089.ml/go/gl01/internal/util/itype" var ( DisplaySize itype.Vec2i // Size of the window viewport in pixels. + ClearColor itype.Vec3f // Clear color of the renderer. + // Directions are not always normalized. ViewPos, ViewDir itype.Vec3d // Position and Direction of the player view. RenderPos, RenderDir itype.Vec3d // Position and Direction of view for the current render pass. Might be different for e.g. lighting passes diff --git a/internal/render/render_world.go b/internal/render/render_world.go index 3dd5098..1574af7 100644 --- a/internal/render/render_world.go +++ b/internal/render/render_world.go @@ -19,26 +19,40 @@ var ( type WorldRenderer struct { lastDisplaySize itype.Vec2i + // Depth mapping pass depthmap struct { fbo, tex uint32 // Framebuffer Object and Texture. shader *Shader // Shader. } + // Geometry pass gbuffer struct { fbo uint32 // The Framebuffer object. - // Textures. Position/Lightspace Depth; Normal/Specular Intensity; Diffuse Color. + // Textures. Position/Lightspace Depth; Normal/Depth; Diffuse Color/Specular Intensity. pos, norm, color uint32 depth uint32 shader *Shader // Geometry pass shaders. } + // Deferred lighting pass + lighting struct { + shader *Shader // Deferred lighting pass shaders + } + + // Semi-transparent pass water struct { - fbo uint32 shader *Shader } - shader *Shader // Deffered lighting pass shaders + // Output pass + output struct { + fbo uint32 // Output framebuffer object. + tex uint32 // Output texture, rendered to the back buffer at the end. + //depth uint32 // Output depth renderbuffer, use gbuffer.depth + shader *Shader // Shader used to copy output.tex to back buffer. + } + texture *Texture // World texture atlas } @@ -48,10 +62,6 @@ var DefaultWorldRenderer WorldRenderer // Init initializes the WorldRenderer. func (r *WorldRenderer) Init(w *world.World) (err error) { - r.shader, err = NewShader(asset.WorldShaderVert, asset.WorldShaderFrag) - if err != nil { - return err - } r.depthmap.shader, err = NewShader(asset.WorldShaderShadowmapVert, asset.WorldShaderShadowmapFrag) if err != nil { return err @@ -60,10 +70,18 @@ func (r *WorldRenderer) Init(w *world.World) (err error) { if err != nil { return err } + r.lighting.shader, err = NewShader(asset.WorldShaderLightingVert, asset.WorldShaderLightingFrag) + if err != nil { + return err + } r.water.shader, err = NewShader(asset.WorldShaderWaterVert, asset.WorldShaderWaterFrag) if err != nil { return err } + r.output.shader, err = NewShader(asset.WorldShaderOutputVert, asset.WorldShaderOutputFrag) + if err != nil { + return err + } asset.InitWorldTextureAtlas() r.texture = NewTextureRGBA(asset.WorldTextureAtlas.Image) @@ -75,8 +93,9 @@ func (r *WorldRenderer) Init(w *world.World) (err error) { r.gbuffer.shader.SetUniformMat4("model", mgl32.Ident4()) r.water.shader.SetUniformMat4("model", mgl32.Ident4()) // and view and projection uniforms not yet set - gl.BindFragDataLocation(r.shader.Handle(), 0, gl.Str("outputColor\x00")) + gl.BindFragDataLocation(r.lighting.shader.Handle(), 0, gl.Str("outputColor\x00")) gl.BindFragDataLocation(r.water.shader.Handle(), 0, gl.Str("outputColor\x00")) + gl.BindFragDataLocation(r.output.shader.Handle(), 0, gl.Str("outputColor\x00")) // generate the depthmap and depthmap FBO gl.GenFramebuffers(1, &r.depthmap.fbo) @@ -95,7 +114,7 @@ func (r *WorldRenderer) Init(w *world.World) (err error) { gl.DrawBuffer(gl.NONE) gl.ReadBuffer(gl.NONE) // attach the shadowmap to the shader - r.shader.SetUniformTextureHandle("shadowmap", r.depthmap.tex) + r.lighting.shader.SetUniformTextureHandle("shadowmap", r.depthmap.tex) r.water.shader.SetUniformTextureHandle("shadowmap", r.depthmap.tex) // generate G-buffer and friends @@ -131,16 +150,24 @@ func (r *WorldRenderer) Init(w *world.World) (err error) { attachments := [...]uint32{gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2} gl.DrawBuffers(int32(len(attachments)), &attachments[0]) // attach the textures - r.shader.SetUniformTextureHandle("gPos", r.gbuffer.pos) - r.shader.SetUniformTextureHandle("gNorm", r.gbuffer.norm) - r.shader.SetUniformTextureHandle("gColor", r.gbuffer.color) + r.lighting.shader.SetUniformTextureHandle("gPos", r.gbuffer.pos) + r.lighting.shader.SetUniformTextureHandle("gNorm", r.gbuffer.norm) + r.lighting.shader.SetUniformTextureHandle("gColor", r.gbuffer.color) - // generate FBO for water rendering - gl.GenFramebuffers(1, &r.water.fbo) - gl.BindFramebuffer(gl.FRAMEBUFFER, r.water.fbo) + // generate the output texture and friends + gl.GenFramebuffers(1, &r.output.fbo) + gl.BindFramebuffer(gl.FRAMEBUFFER, r.output.fbo) + // output + gl.GenTextures(1, &r.output.tex) + gl.BindTexture(gl.TEXTURE_2D, r.output.tex) + gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(io.DisplaySize[0]), int32(io.DisplaySize[1]), 0, gl.RGBA, gl.UNSIGNED_BYTE, nil) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) + gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, r.output.tex, 0) + // depth gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, r.gbuffer.depth) - gl.DrawBuffer(gl.BACK) - gl.ReadBuffer(gl.BACK) + // attach textures + r.output.shader.SetUniformTextureHandle("tex", r.output.tex) gl.BindFramebuffer(gl.FRAMEBUFFER, 0) r.lastDisplaySize = io.DisplaySize @@ -172,6 +199,8 @@ func (r *WorldRenderer) Render(world *world.World, view *View) { gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(io.DisplaySize[0]), int32(io.DisplaySize[1]), 0, gl.RGBA, gl.UNSIGNED_BYTE, nil) gl.BindRenderbuffer(gl.RENDERBUFFER, r.gbuffer.depth) gl.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, int32(io.DisplaySize[0]), int32(io.DisplaySize[1])) + gl.BindTexture(gl.TEXTURE_2D, r.output.tex) + gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(io.DisplaySize[0]), int32(io.DisplaySize[1]), 0, gl.RGBA, gl.UNSIGNED_BYTE, nil) r.lastDisplaySize = io.DisplaySize } @@ -221,16 +250,17 @@ func (r *WorldRenderer) Render(world *world.World, view *View) { world.Render() - gl.BindFramebuffer(gl.FRAMEBUFFER, 0) - // 3. Render the actual output with deferred lighting + gl.BindFramebuffer(gl.FRAMEBUFFER, r.output.fbo) + gl.ClearColor(io.ClearColor[0], io.ClearColor[1], io.ClearColor[2], 1) + gl.Clear(gl.COLOR_BUFFER_BIT) gl.Disable(gl.DEPTH_TEST) - r.shader.UseProgram() - r.shader.BindTextures() - r.shader.SetUniformMat4("lightspace", lightspace) - r.shader.SetUniformVec3f("viewPos", view.EyePos) - r.shader.SetUniformVec4f("fogColor", itype.Vec4f{0.6, 0.8, 1.0, 1.0}) - r.shader.SetUniformVec3f("sun", normalSun) + r.lighting.shader.UseProgram() + r.lighting.shader.BindTextures() + r.lighting.shader.SetUniformMat4("lightspace", lightspace) + r.lighting.shader.SetUniformVec3f("viewPos", view.EyePos) + r.lighting.shader.SetUniformVec4f("fogColor", itype.Vec4f{0.6, 0.8, 1.0, 1.0}) + r.lighting.shader.SetUniformVec3f("sun", normalSun) DrawScreenQuad() @@ -241,7 +271,6 @@ func (r *WorldRenderer) Render(world *world.World, view *View) { gl.Enable(gl.BLEND) gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) gl.BlendEquation(gl.FUNC_ADD) - gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, r.gbuffer.depth) r.water.shader.UseProgram() r.water.shader.BindTextures() @@ -249,11 +278,21 @@ func (r *WorldRenderer) Render(world *world.World, view *View) { r.water.shader.SetUniformMat4("view", view.View()) r.water.shader.SetUniformMat4("projection", view.Perspective()) r.water.shader.SetUniformVec3f("viewPos", view.EyePos) + r.water.shader.SetUniformVec4f("fogColor", itype.Vec4f{0.6, 0.8, 1.0, 1.0}) r.water.shader.SetUniformVec3f("sun", normalSun) r.water.shader.SetUniformFloat("alpha", alpha) world.RenderWater() + // Finally. Copy the output texture to the back buffer + gl.BindFramebuffer(gl.FRAMEBUFFER, 0) + gl.Disable(gl.DEPTH_TEST) + gl.Disable(gl.BLEND) + r.output.shader.UseProgram() + r.output.shader.BindTextures() + + DrawScreenQuad() + // Show G-buffers? /*if io.ShowDebugInfo { DrawTexture(r.gbuffer.pos, itype.Rectf{0.5, 0.5, 0.5, 0.5}, DrawTextureChannels_R|DrawTextureChannels_G|DrawTextureChannels_B, 0, 32) diff --git a/internal/util/itype/vec.go b/internal/util/itype/vec.go index 0948ab0..fcb1237 100644 --- a/internal/util/itype/vec.go +++ b/internal/util/itype/vec.go @@ -7,7 +7,7 @@ import ( // Vec2i is a two-element int vector type Vec2i [2]int -func Vec2iToFloat32(v Vec2i) Vec2f { return Vec2f{float32(v[0]), float32(v[1])} } +func (v Vec2i) ToFloat32() Vec2f { return Vec2f{float32(v[0]), float32(v[1])} } func (v Vec2i) Add(add Vec2i) Vec2i { return Vec2i{v[0] + add[0], v[1] + add[1]} } func (v Vec2i) MultiplyInt(mult int) Vec2i { return Vec2i{v[0] * mult, v[1] * mult} }