After all why not, why shouldn't we have HDR

This commit is contained in:
bloeys
2024-05-12 06:46:46 +04:00
parent 7b1e3ea7b4
commit cf6b2655e7
3 changed files with 201 additions and 24 deletions

View File

@ -42,6 +42,7 @@ const (
FramebufferAttachmentDataFormat_Unknown FramebufferAttachmentDataFormat = iota FramebufferAttachmentDataFormat_Unknown FramebufferAttachmentDataFormat = iota
FramebufferAttachmentDataFormat_R32Int FramebufferAttachmentDataFormat_R32Int
FramebufferAttachmentDataFormat_RGBA8 FramebufferAttachmentDataFormat_RGBA8
FramebufferAttachmentDataFormat_RGBAF16
FramebufferAttachmentDataFormat_SRGBA FramebufferAttachmentDataFormat_SRGBA
FramebufferAttachmentDataFormat_DepthF32 FramebufferAttachmentDataFormat_DepthF32
FramebufferAttachmentDataFormat_Depth24Stencil8 FramebufferAttachmentDataFormat_Depth24Stencil8
@ -50,7 +51,8 @@ const (
func (f FramebufferAttachmentDataFormat) IsColorFormat() bool { func (f FramebufferAttachmentDataFormat) IsColorFormat() bool {
return f == FramebufferAttachmentDataFormat_R32Int || return f == FramebufferAttachmentDataFormat_R32Int ||
f == FramebufferAttachmentDataFormat_RGBA8 || f == FramebufferAttachmentDataFormat_RGBA8 ||
f == FramebufferAttachmentDataFormat_SRGBA f == FramebufferAttachmentDataFormat_SRGBA ||
f == FramebufferAttachmentDataFormat_RGBAF16
} }
func (f FramebufferAttachmentDataFormat) IsDepthFormat() bool { func (f FramebufferAttachmentDataFormat) IsDepthFormat() bool {
@ -65,6 +67,8 @@ func (f FramebufferAttachmentDataFormat) GlInternalFormat() int32 {
return gl.R32I return gl.R32I
case FramebufferAttachmentDataFormat_RGBA8: case FramebufferAttachmentDataFormat_RGBA8:
return gl.RGB8 return gl.RGB8
case FramebufferAttachmentDataFormat_RGBAF16:
return gl.RGBA16F
case FramebufferAttachmentDataFormat_SRGBA: case FramebufferAttachmentDataFormat_SRGBA:
return gl.SRGB_ALPHA return gl.SRGB_ALPHA
case FramebufferAttachmentDataFormat_DepthF32: case FramebufferAttachmentDataFormat_DepthF32:
@ -85,6 +89,8 @@ func (f FramebufferAttachmentDataFormat) GlFormat() uint32 {
case FramebufferAttachmentDataFormat_RGBA8: case FramebufferAttachmentDataFormat_RGBA8:
fallthrough fallthrough
case FramebufferAttachmentDataFormat_RGBAF16:
fallthrough
case FramebufferAttachmentDataFormat_SRGBA: case FramebufferAttachmentDataFormat_SRGBA:
return gl.RGBA return gl.RGBA
@ -100,6 +106,33 @@ func (f FramebufferAttachmentDataFormat) GlFormat() uint32 {
} }
} }
func (f FramebufferAttachmentDataFormat) GlComponentType() uint32 {
switch f {
case FramebufferAttachmentDataFormat_R32Int:
return gl.INT
case FramebufferAttachmentDataFormat_RGBA8:
fallthrough
case FramebufferAttachmentDataFormat_SRGBA:
return gl.UNSIGNED_BYTE
case FramebufferAttachmentDataFormat_RGBAF16:
// Seems this is fine to be float instead of half float
fallthrough
case FramebufferAttachmentDataFormat_DepthF32:
return gl.FLOAT
case FramebufferAttachmentDataFormat_Depth24Stencil8:
return gl.UNSIGNED_INT_24_8
default:
logging.ErrLog.Fatalf("unknown framebuffer attachment data format. Format=%d\n", f)
return 0
}
}
type FramebufferAttachment struct { type FramebufferAttachment struct {
Id uint32 Id uint32
Type FramebufferAttachmentType Type FramebufferAttachmentType
@ -124,7 +157,7 @@ func (fbo *Framebuffer) BindWithViewport() {
gl.Viewport(0, 0, int32(fbo.Width), int32(fbo.Height)) gl.Viewport(0, 0, int32(fbo.Width), int32(fbo.Height))
} }
// Clear calls gl.Clear with the fob's clear flags. // Clear calls gl.Clear with the fbo's clear flags.
// Note that the fbo must be complete and bound. // Note that the fbo must be complete and bound.
// Calling this without a bound fbo will clear something else, like your screen. // Calling this without a bound fbo will clear something else, like your screen.
func (fbo *Framebuffer) Clear() { func (fbo *Framebuffer) Clear() {
@ -207,7 +240,17 @@ func (fbo *Framebuffer) NewColorAttachment(
} }
gl.BindTexture(gl.TEXTURE_2D, a.Id) gl.BindTexture(gl.TEXTURE_2D, a.Id)
gl.TexImage2D(gl.TEXTURE_2D, 0, attachFormat.GlInternalFormat(), int32(fbo.Width), int32(fbo.Height), 0, attachFormat.GlFormat(), gl.UNSIGNED_BYTE, nil) gl.TexImage2D(
gl.TEXTURE_2D,
0,
attachFormat.GlInternalFormat(),
int32(fbo.Width),
int32(fbo.Height),
0,
attachFormat.GlFormat(),
attachFormat.GlComponentType(),
nil,
)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
@ -298,7 +341,17 @@ func (fbo *Framebuffer) NewDepthAttachment(
} }
gl.BindTexture(gl.TEXTURE_2D, a.Id) gl.BindTexture(gl.TEXTURE_2D, a.Id)
gl.TexImage2D(gl.TEXTURE_2D, 0, attachFormat.GlInternalFormat(), int32(fbo.Width), int32(fbo.Height), 0, attachFormat.GlFormat(), gl.FLOAT, nil) gl.TexImage2D(
gl.TEXTURE_2D,
0,
attachFormat.GlInternalFormat(),
int32(fbo.Width),
int32(fbo.Height),
0,
attachFormat.GlFormat(),
attachFormat.GlComponentType(),
nil,
)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
@ -341,7 +394,17 @@ func (fbo *Framebuffer) NewDepthAttachment(
gl.BindTexture(gl.TEXTURE_CUBE_MAP, a.Id) gl.BindTexture(gl.TEXTURE_CUBE_MAP, a.Id)
for i := 0; i < 6; i++ { for i := 0; i < 6; i++ {
gl.TexImage2D(uint32(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i), 0, attachFormat.GlInternalFormat(), int32(fbo.Width), int32(fbo.Height), 0, attachFormat.GlFormat(), gl.FLOAT, nil) gl.TexImage2D(
uint32(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i),
0,
attachFormat.GlInternalFormat(),
int32(fbo.Width),
int32(fbo.Height),
0,
attachFormat.GlFormat(),
attachFormat.GlComponentType(),
nil,
)
} }
gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
@ -398,7 +461,7 @@ func (fbo *Framebuffer) NewDepthCubemapArrayAttachment(
6*numCubemaps, 6*numCubemaps,
0, 0,
attachFormat.GlFormat(), attachFormat.GlFormat(),
gl.FLOAT, attachFormat.GlComponentType(),
nil, nil,
) )
@ -455,7 +518,7 @@ func (fbo *Framebuffer) NewDepthTextureArrayAttachment(
numTextures, numTextures,
0, 0,
attachFormat.GlFormat(), attachFormat.GlFormat(),
gl.FLOAT, attachFormat.GlComponentType(),
nil, nil,
) )
@ -513,7 +576,17 @@ func (fbo *Framebuffer) NewDepthStencilAttachment(
} }
gl.BindTexture(gl.TEXTURE_2D, a.Id) gl.BindTexture(gl.TEXTURE_2D, a.Id)
gl.TexImage2D(gl.TEXTURE_2D, 0, attachFormat.GlInternalFormat(), int32(fbo.Width), int32(fbo.Height), 0, attachFormat.GlFormat(), gl.UNSIGNED_INT_24_8, nil) gl.TexImage2D(
gl.TEXTURE_2D,
0,
attachFormat.GlInternalFormat(),
int32(fbo.Width),
int32(fbo.Height),
0,
attachFormat.GlFormat(),
attachFormat.GlComponentType(),
nil,
)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)

86
main.go
View File

@ -36,9 +36,10 @@ import (
- Point light shadows ✅ - Point light shadows ✅
- Spotlight shadows ✅ - Spotlight shadows ✅
- Create VAO struct independent from VBO to support multi-VBO use cases (e.g. instancing) ✅ - Create VAO struct independent from VBO to support multi-VBO use cases (e.g. instancing) ✅
- Normals maps - Normals maps
- HDR ✅
- Fix bad point light acne
- UBO support - UBO support
- HDR
- Cascaded shadow mapping - Cascaded shadow mapping
- Skeletal animations - Skeletal animations
- In some cases we DO want input even when captured by UI. We need two systems within input package, one filtered and one not✅ - In some cases we DO want input even when captured by UI. We need two systems within input package, one filtered and one not✅
@ -202,12 +203,13 @@ var (
yaw float32 = -1.5 yaw float32 = -1.5
cam camera.Camera cam camera.Camera
// Demo fbo
renderToDemoFbo = false
renderToBackBuffer = true renderToBackBuffer = true
demoFboScale = gglm.NewVec2(0.25, 0.25)
demoFboOffset = gglm.NewVec2(0.75, -0.75) // Demo fbo
demoFbo buffers.Framebuffer renderToDemoFbo = false
demoFboScale = gglm.NewVec2(0.25, 0.25)
demoFboOffset = gglm.NewVec2(0.75, -0.75)
demoFbo buffers.Framebuffer
// Dir light fbo // Dir light fbo
showDirLightDepthMapFbo = false showDirLightDepthMapFbo = false
@ -221,6 +223,12 @@ var (
// Spot light fbo // Spot light fbo
spotLightDepthMapFbo buffers.Framebuffer spotLightDepthMapFbo buffers.Framebuffer
// Hdr Fbo
hdrRendering = true
hdrExposure float32 = 1
tonemappedScreenQuadMat materials.Material
hdrFbo buffers.Framebuffer
screenQuadVao buffers.VertexArray screenQuadVao buffers.VertexArray
screenQuadMat materials.Material screenQuadMat materials.Material
@ -243,7 +251,7 @@ var (
cubeModelMat = gglm.NewTrMatId() cubeModelMat = gglm.NewTrMatId()
renderSkybox = true renderSkybox = true
renderDepthBuffer bool renderDepthBuffer = false
skyboxCmap assets.Cubemap skyboxCmap assets.Cubemap
@ -518,6 +526,9 @@ func (g *Game) Init() {
screenQuadMat.SetUnifVec2("offset", &demoFboOffset) screenQuadMat.SetUnifVec2("offset", &demoFboOffset)
screenQuadMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse)) screenQuadMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse))
tonemappedScreenQuadMat = materials.NewMaterial("Tonemapped Screen Quad Mat", "./res/shaders/tonemapped-screen-quad.glsl")
tonemappedScreenQuadMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse))
unlitMat = materials.NewMaterial("Unlit mat", "./res/shaders/simple-unlit.glsl") unlitMat = materials.NewMaterial("Unlit mat", "./res/shaders/simple-unlit.glsl")
unlitMat.Settings.Set(materials.MaterialSettings_HasModelMtx) unlitMat.Settings.Set(materials.MaterialSettings_HasModelMtx)
unlitMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse)) unlitMat.SetUnifInt32("material.diffuse", int32(materials.TextureSlot_Diffuse))
@ -635,6 +646,8 @@ func (g *Game) Init() {
func (g *Game) initFbos() { func (g *Game) initFbos() {
// @TODO: Resize window sized fbos on window resize
// Demo fbo // Demo fbo
demoFbo = buffers.NewFramebuffer(uint32(g.WinWidth), uint32(g.WinHeight)) demoFbo = buffers.NewFramebuffer(uint32(g.WinWidth), uint32(g.WinHeight))
@ -679,6 +692,20 @@ func (g *Game) initFbos() {
) )
assert.T(spotLightDepthMapFbo.IsComplete(), "Spot light depth map fbo is not complete after init") assert.T(spotLightDepthMapFbo.IsComplete(), "Spot light depth map fbo is not complete after init")
// Hdr fbo
hdrFbo = buffers.NewFramebuffer(uint32(g.WinWidth), uint32(g.WinHeight))
hdrFbo.NewColorAttachment(
buffers.FramebufferAttachmentType_Texture,
buffers.FramebufferAttachmentDataFormat_RGBAF16,
)
hdrFbo.NewDepthStencilAttachment(
buffers.FramebufferAttachmentType_Renderbuffer,
buffers.FramebufferAttachmentDataFormat_Depth24Stencil8,
)
assert.T(hdrFbo.IsComplete(), "Hdr fbo is not complete after init")
} }
func (g *Game) updateLights() { func (g *Game) updateLights() {
@ -819,6 +846,14 @@ func (g *Game) showDebugWindow() {
imgui.Spacing() imgui.Spacing()
imgui.Text("HDR")
imgui.Checkbox("Enable HDR", &hdrRendering)
if imgui.DragFloat("Exposure", &hdrExposure) {
tonemappedScreenQuadMat.SetUnifFloat32("exposure", hdrExposure)
}
imgui.Spacing()
// Ambient light // Ambient light
imgui.Text("Ambient Light") imgui.Text("Ambient Light")
@ -1075,8 +1110,8 @@ func (g *Game) updateCameraPos() {
} }
var ( var (
renderDirLightShadows = false renderDirLightShadows = true
renderPointLightShadows = false renderPointLightShadows = true
renderSpotLightShadows = true renderSpotLightShadows = true
rotatingCubeSpeedDeg1 float32 = 45 rotatingCubeSpeedDeg1 float32 = 45
@ -1114,17 +1149,19 @@ func (g *Game) Render() {
if renderDepthBuffer { if renderDepthBuffer {
g.RenderScene(&debugDepthMat) g.RenderScene(&debugDepthMat)
} else if hdrRendering {
g.renderHdrFbo()
} else { } else {
g.RenderScene(nil) g.RenderScene(nil)
if renderSkybox {
g.DrawSkybox()
}
} }
} }
if renderSkybox {
g.DrawSkybox()
}
if renderToDemoFbo { if renderToDemoFbo {
g.renderDemoFob() g.renderDemoFbo()
} }
} }
@ -1222,7 +1259,7 @@ func (g *Game) renderPointLightShadowmaps() {
pointLightDepthMapFbo.UnBindWithViewport(uint32(g.WinWidth), uint32(g.WinHeight)) pointLightDepthMapFbo.UnBindWithViewport(uint32(g.WinWidth), uint32(g.WinHeight))
} }
func (g *Game) renderDemoFob() { func (g *Game) renderDemoFbo() {
demoFbo.Bind() demoFbo.Bind()
demoFbo.Clear() demoFbo.Clear()
@ -1246,6 +1283,23 @@ func (g *Game) renderDemoFob() {
window.Rend.DrawVertexArray(&screenQuadMat, &screenQuadVao, 0, 6) window.Rend.DrawVertexArray(&screenQuadMat, &screenQuadVao, 0, 6)
} }
func (g *Game) renderHdrFbo() {
hdrFbo.Bind()
hdrFbo.Clear()
g.RenderScene(nil)
if renderSkybox {
g.DrawSkybox()
}
hdrFbo.UnBind()
tonemappedScreenQuadMat.DiffuseTex = hdrFbo.Attachments[0].Id
window.Rend.DrawVertexArray(&tonemappedScreenQuadMat, &screenQuadVao, 0, 6)
}
func (g *Game) RenderScene(overrideMat *materials.Material) { func (g *Game) RenderScene(overrideMat *materials.Material) {
tempModelMatrix := cubeModelMat.Clone() tempModelMatrix := cubeModelMat.Clone()

View File

@ -0,0 +1,50 @@
//shader:vertex
#version 410
out vec2 vertUV0;
// Hardcoded vertex positions for a fullscreen quad.
// Format: vec4(pos.x, pos.y, uv0.x, uv0.y)
vec4 quadData[6] = vec4[](
vec4(-1.0, 1.0, 0.0, 1.0),
vec4(-1.0, -1.0, 0.0, 0.0),
vec4(1.0, -1.0, 1.0, 0.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4(1.0, -1.0, 1.0, 0.0),
vec4(1.0, 1.0, 1.0, 1.0)
);
void main()
{
vec4 vertData = quadData[gl_VertexID];
vertUV0 = vertData.zw;
gl_Position = vec4(vertData.xy, 0.0, 1.0);
}
//shader:fragment
#version 410
struct Material {
sampler2D diffuse;
};
uniform float exposure = 1;
uniform Material material;
in vec2 vertUV0;
out vec4 fragColor;
void main()
{
vec4 diffuseTexColor = texture(material.diffuse, vertUV0);
// Reinhard tone mapping
// vec3 mappedColor = diffuseTexColor.rgb / (diffuseTexColor.rgb + vec3(1.0));
// Exposure tone mapping
vec3 mappedColor = vec3(1.0) - exp(-diffuseTexColor.rgb * exposure);
fragColor = vec4(mappedColor, 1);
}