semi-transparent water rendering (TODO)

This commit is contained in:
Edgaru089 2022-01-29 00:43:11 +08:00
parent 904221ac14
commit fea09c5012
18 changed files with 250 additions and 68 deletions

View File

@ -2,23 +2,27 @@ package asset
import _ "embed"
//go:embed shader/world.frag
var WorldShaderFrag string
var (
//go:embed shader/world/output.frag
WorldShaderFrag string
//go:embed shader/world/output.vert
WorldShaderVert string
//go:embed shader/world.vert
var WorldShaderVert string
//go:embed shader/world/shadowmap.frag
WorldShaderShadowmapFrag string
//go:embed shader/world/shadowmap.vert
WorldShaderShadowmapVert string
//go:embed shader/world.shadowmap.frag
var WorldShaderShadowmapFrag string
//go:embed shader/world/geometry.frag
WorldShaderGeometryFrag string
//go:embed shader/world/geometry.vert
WorldShaderGeometryVert string
//go:embed shader/world.shadowmap.vert
var WorldShaderShadowmapVert string
//go:embed shader/world.geometry.frag
var WorldShaderGeometryFrag string
//go:embed shader/world.geometry.vert
var WorldShaderGeometryVert string
//go:embed shader/world/water.frag
WorldShaderWaterFrag string
//go:embed shader/world/water.vert
WorldShaderWaterVert string
)
//go:embed shader/framewire.frag
var FramewireShaderFrag string

View File

@ -24,7 +24,7 @@ out vec4 outputColor;
const float gamma = 2.2;
const float ambient = 0.3, specularStrength = 0.5, specularShininess = 32;
const float ambient = 0.3, specularStrength = 0.08, specularShininess = 8;
const float fogDensity = .00003;

View File

@ -0,0 +1,93 @@
#version 330
uniform sampler2D tex;
uniform sampler2D shadowmap;
uniform vec3 viewPos;
uniform vec3 sun;
uniform vec4 fogColor;
uniform float alpha; // Alpha of the semi-transparant layer
// Fragment information
in vec4 fragPos;
in vec4 fragPosLightspace;
in vec3 fragNormal;
in vec2 fragTexCoord;
vec4 fragColor;
out vec4 outputColor;
const float gamma = 2.2;
const float ambient = 0.3, specularStrength = 1.6, specularShininess = 128;
const float fogDensity = .00003;
float finalpha;
float light;
vec4 texpixel, color;
void lightSun();
float lightSunShadow();
void lightPoint(int i);
void main() {
fragColor = vec4(pow(texture(tex, fragTexCoord).rgb, vec3(gamma)), 1.0f);
finalpha = alpha;
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) * finalpha;
}
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);
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;
finalpha = min(finalpha + 0.1f * shadow, 1.0f);
}
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 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);
}

View File

@ -0,0 +1,26 @@
#version 330
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform mat4 lightspace;
layout (location = 0) in vec3 vert;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texCoord;
layout (location = 4) in float light;
out vec4 fragPos;
out vec4 fragPosLightspace;
out vec3 fragNormal;
out vec2 fragTexCoord;
void main() {
fragTexCoord = texCoord;
fragPos = model * vec4(vert, 1);
fragPosLightspace = lightspace * fragPos;
fragNormal = normal;
gl_Position = projection * view * fragPos;
}

Binary file not shown.

View File

@ -45,35 +45,6 @@ func (e *Entity) boxHitpoints(points []itype.Vec3d, hitbox itype.Boxd) []itype.V
base.Add(itype.Vec3d{box.SizeX, 0, box.SizeZ}),
base.Add(itype.Vec3d{box.SizeX, box.SizeY, box.SizeZ}),
)
/*
// add the surface points
// X+ and X-
for y := base[1] + HitpointMeshLen; y < base[1]+hitbox[1]; y += HitpointMeshLen {
for z := base[2] + HitpointMeshLen; z < base[2]+hitbox[2]; z += HitpointMeshLen {
points = append(points,
base.Addv(0, y, z),
base.Addv(box.SizeX, y, z),
)
}
}
// Y+ and Y-
for x := base[0] + HitpointMeshLen; x < base[0]+hitbox[0]; x += HitpointMeshLen {
for z := base[2] + HitpointMeshLen; z < base[2]+hitbox[2]; z += HitpointMeshLen {
points = append(points,
base.Addv(x, 0, z),
base.Addv(x, box.SizeY, z),
)
}
}
// Z+ and Z-
for x := base[0] + HitpointMeshLen; x < base[0]+hitbox[0]; x += HitpointMeshLen {
for y := base[1] + HitpointMeshLen; y < base[1]+hitbox[1]; y += HitpointMeshLen {
points = append(points,
base.Addv(x, y, 0),
base.Addv(x, y, box.SizeZ),
)
}
}*/
return points
}
@ -89,6 +60,7 @@ func pointStuck(point itype.Vec3d, w *world.World) bool {
point.Floor(),
block.Aux,
block.Dataset,
w,
).Hitbox.Offset(blockid.ToFloat64()).Contains(point)
}
@ -123,7 +95,7 @@ func (e *Entity) moveX(delta float64, hitbox itype.Boxd, w *world.World) {
if block.Id == 0 {
deltaDone = delta
} else { // block.Id!=0
app := world.GetBlockAppearance(blockid, block.Id, block.Aux, block.Dataset)
app := world.GetBlockAppearance(blockid, block.Id, block.Aux, block.Dataset, w)
blockBox := app.Hitbox.Offset(blockid.ToFloat64())
if !app.NotSolid && blockBox.Contains(dest) { // Hit!
hit = true
@ -176,7 +148,7 @@ func (e *Entity) moveY(delta float64, hitbox itype.Boxd, w *world.World) {
if block.Id == 0 {
deltaDone = delta
} else { // block.Id!=0
app := world.GetBlockAppearance(blockid, block.Id, block.Aux, block.Dataset)
app := world.GetBlockAppearance(blockid, block.Id, block.Aux, block.Dataset, w)
blockBox := app.Hitbox.Offset(blockid.ToFloat64())
if !app.NotSolid && blockBox.Contains(dest) { // Hit!
hit = true
@ -240,7 +212,7 @@ func (e *Entity) moveZ(delta float64, hitbox itype.Boxd, w *world.World) {
if block.Id == 0 {
deltaDone = delta
} else { // block.Id!=0
app := world.GetBlockAppearance(blockid, block.Id, block.Aux, block.Dataset)
app := world.GetBlockAppearance(blockid, block.Id, block.Aux, block.Dataset, w)
blockBox := app.Hitbox.Offset(blockid.ToFloat64())
if !app.NotSolid && blockBox.Contains(dest) { // Hit!
hit = true

View File

@ -47,8 +47,8 @@ func (g *Game) Init(win *glfw.Window) {
var seed int64 = time.Now().Unix()
gensync := make(chan struct{})
gensynccnt := 0
for i := -8; i <= 8; i++ {
for j := -8; j <= 8; j++ {
for i := -4; i <= 4; i++ {
for j := -4; j <= 4; j++ {
c := &world.Chunk{}
g.world.SetChunk(i, j, c)
go func() {

View File

@ -33,6 +33,11 @@ type WorldRenderer struct {
shader *Shader // Geometry pass shaders.
}
water struct {
fbo uint32
shader *Shader
}
shader *Shader // Deffered lighting pass shaders
texture *Texture // World texture atlas
}
@ -55,16 +60,23 @@ func (r *WorldRenderer) Init(w *world.World) (err error) {
if err != nil {
return err
}
r.water.shader, err = NewShader(asset.WorldShaderWaterVert, asset.WorldShaderWaterFrag)
if err != nil {
return err
}
asset.InitWorldTextureAtlas()
r.texture = NewTextureRGBA(asset.WorldTextureAtlas.Image)
r.texture.GenerateMipMap()
r.gbuffer.shader.SetUniformTexture("tex", r.texture)
r.water.shader.SetUniformTexture("tex", r.texture)
r.depthmap.shader.SetUniformMat4("model", mgl32.Ident4())
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.water.shader.Handle(), 0, gl.Str("outputColor\x00"))
// generate the depthmap and depthmap FBO
gl.GenFramebuffers(1, &r.depthmap.fbo)
@ -84,6 +96,7 @@ func (r *WorldRenderer) Init(w *world.World) (err error) {
gl.ReadBuffer(gl.NONE)
// attach the shadowmap to the shader
r.shader.SetUniformTextureHandle("shadowmap", r.depthmap.tex)
r.water.shader.SetUniformTextureHandle("shadowmap", r.depthmap.tex)
// generate G-buffer and friends
gl.GenFramebuffers(1, &r.gbuffer.fbo)
@ -122,6 +135,13 @@ func (r *WorldRenderer) Init(w *world.World) (err error) {
r.shader.SetUniformTextureHandle("gNorm", r.gbuffer.norm)
r.shader.SetUniformTextureHandle("gColor", r.gbuffer.color)
// generate FBO for water rendering
gl.GenFramebuffers(1, &r.water.fbo)
gl.BindFramebuffer(gl.FRAMEBUFFER, r.water.fbo)
gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, r.gbuffer.depth)
gl.DrawBuffer(gl.BACK)
gl.ReadBuffer(gl.BACK)
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
r.lastDisplaySize = io.DisplaySize
@ -136,6 +156,7 @@ func (r *WorldRenderer) ResizeDisplay(newSize itype.Vec2i) {
}
var sun = [3]float32{0.2, 0.4, 0.3}
var alpha float32 = 0.55
func (r *WorldRenderer) Render(world *world.World, view *View) {
io.RenderPos = io.ViewPos
@ -149,13 +170,14 @@ func (r *WorldRenderer) Render(world *world.World, view *View) {
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, int32(io.DisplaySize[0]), int32(io.DisplaySize[1]), 0, gl.RGBA, gl.FLOAT, nil)
gl.BindTexture(gl.TEXTURE_2D, r.gbuffer.color)
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.norm)
gl.BindRenderbuffer(gl.RENDERBUFFER, r.gbuffer.depth)
gl.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, int32(io.DisplaySize[0]), int32(io.DisplaySize[1]))
r.lastDisplaySize = io.DisplaySize
}
imgui.SliderFloat3("Sun", &sun, -1, 1)
normalSun := itype.Vec3f(sun).Normalize()
imgui.SliderFloat("Water Alpha", &alpha, 0, 1)
gl.Enable(gl.CULL_FACE)
gl.Enable(gl.DEPTH_TEST)
@ -178,6 +200,7 @@ func (r *WorldRenderer) Render(world *world.World, view *View) {
r.depthmap.shader.SetUniformMat4("lightspace", lightspace)
world.Render()
world.RenderWater()
// 2. Geometry pass, render to G-buffer
io.RenderPos = io.ViewPos
@ -201,10 +224,9 @@ func (r *WorldRenderer) Render(world *world.World, view *View) {
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
// 3. Render the actual output with deferred lighting
gl.Clear(gl.DEPTH_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})
@ -212,6 +234,26 @@ func (r *WorldRenderer) Render(world *world.World, view *View) {
DrawScreenQuad()
// 4. Render water
gl.Enable(gl.DEPTH_TEST)
gl.DepthFunc(gl.LESS)
gl.Enable(gl.CULL_FACE)
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()
r.water.shader.SetUniformMat4("lightspace", lightspace)
r.water.shader.SetUniformMat4("view", view.View())
r.water.shader.SetUniformMat4("projection", view.Perspective())
r.water.shader.SetUniformVec3f("viewPos", view.EyePos)
r.water.shader.SetUniformVec3f("sun", normalSun)
r.water.shader.SetUniformFloat("alpha", alpha)
world.RenderWater()
// 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)

View File

@ -36,7 +36,8 @@ type BlockAppearance struct {
data itype.Dataset,
world *World,
vertexArray []Vertex,
) []Vertex
vertexArrayWater []Vertex,
) (verts []Vertex, waters []Vertex)
}
// BlockBehaviour describes a kind of block of the same Major ID.

View File

@ -10,7 +10,7 @@ type WaterBehaviour struct{}
func (WaterBehaviour) Static() bool { return false }
func (WaterBehaviour) RequireDataset() bool { return false }
func (WaterBehaviour) RequireBlockUpdate() bool { return false }
func (b WaterBehaviour) Appearance(position itype.Vec3i, aux int, data itype.Dataset, w *world.World) world.BlockAppearance {
func (WaterBehaviour) Appearance(position itype.Vec3i, aux int, data itype.Dataset, w *world.World) world.BlockAppearance {
return world.BlockAppearance{
Name: "water",
Transparent: true,
@ -22,13 +22,13 @@ func (b WaterBehaviour) Appearance(position itype.Vec3i, aux int, data itype.Dat
aux int,
data itype.Dataset,
w *world.World,
vertexArray []world.Vertex) []world.Vertex {
vertexArray []world.Vertex, vertsWater []world.Vertex) (verts, waters []world.Vertex) {
if block := w.Block(position.Addv(0, 1, 0)); block.Id != Water {
return appendFace(itype.YPlus, position, "water.png", itype.Vec3f{0, -0.125, 0}, vertexArray)
return vertexArray, appendFace(itype.YPlus, position, "water.png", itype.Vec3f{0, -0.125, 0}, vertsWater)
}
return vertexArray
return vertexArray, vertsWater
},
}
}

View File

@ -25,7 +25,7 @@ type Chunk struct {
vao, vbo uint32
vbolen int
}
vertUpdate chan []Vertex
vertUpdate chan [2][]Vertex
world *World
}

View File

@ -34,7 +34,21 @@ func (c *Chunk) InitRender() {
gl.EnableVertexAttribArray(2)
gl.EnableVertexAttribArray(3)
c.vertUpdate = make(chan []Vertex, 2)
gl.GenVertexArrays(1, &c.water.vao)
gl.BindVertexArray(c.water.vao)
gl.GenBuffers(1, &c.water.vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, c.water.vbo)
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, int32(unsafe.Sizeof(Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(Vertex{}.World))))
gl.VertexAttribPointer(1, 3, gl.FLOAT, false, int32(unsafe.Sizeof(Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(Vertex{}.Normal))))
gl.VertexAttribPointer(2, 2, gl.FLOAT, false, int32(unsafe.Sizeof(Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(Vertex{}.Texture))))
gl.VertexAttribPointer(3, 1, gl.FLOAT, false, int32(unsafe.Sizeof(Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(Vertex{}.Light))))
gl.EnableVertexAttribArray(0)
gl.EnableVertexAttribArray(1)
gl.EnableVertexAttribArray(2)
gl.EnableVertexAttribArray(3)
c.vertUpdate = make(chan [2][]Vertex, 2)
c.renderChanged = true
}
@ -42,6 +56,8 @@ func (c *Chunk) InitRender() {
func (c *Chunk) FreeRender() {
gl.DeleteVertexArrays(1, &c.vao)
gl.DeleteBuffers(1, &c.vbo)
gl.DeleteVertexArrays(1, &c.water.vao)
gl.DeleteBuffers(1, &c.water.vbo)
close(c.vertUpdate)
}
@ -51,6 +67,12 @@ func (c *Chunk) Bind() {
gl.BindBuffer(gl.ARRAY_BUFFER, c.vbo)
}
// BindWater binds the semi-transparant layer of the renderer.
func (c *Chunk) BindWater() {
gl.BindVertexArray(c.water.vao)
gl.BindBuffer(gl.ARRAY_BUFFER, c.water.vbo)
}
// a global vertex array for VBO updates
//var vertex []Vertex
@ -59,17 +81,21 @@ func (c *Chunk) updateRender() {
if c.renderChanged {
go func() {
t := time.Now()
vert := c.AppendVertex([]Vertex{})
vert, vertWater := c.appendVertex([]Vertex{}, []Vertex{})
log.Printf("Chunk [%d,%d]: UpdateRender: vertex len of %d*%d = %d in %dms", c.X, c.Z, unsafe.Sizeof(Vertex{}), len(vert), int(unsafe.Sizeof(Vertex{}))*len(vert), time.Since(t).Milliseconds())
c.vertUpdate <- vert
c.vertUpdate <- [2][]Vertex{vert, vertWater}
}()
c.renderChanged = false
}
select {
case vert := <-c.vertUpdate:
gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(Vertex{}))*len(vert), gl.Ptr(vert), gl.DYNAMIC_DRAW)
c.vbolen = len(vert)
gl.BindBuffer(gl.ARRAY_BUFFER, c.vbo)
gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(Vertex{}))*len(vert[0]), gl.Ptr(vert[0]), gl.DYNAMIC_DRAW)
gl.BindBuffer(gl.ARRAY_BUFFER, c.water.vbo)
gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(Vertex{}))*len(vert[1]), gl.Ptr(vert[1]), gl.DYNAMIC_DRAW)
c.vbolen = len(vert[0])
c.water.vbolen = len(vert[1])
default: // do nothing
}
}
@ -107,13 +133,23 @@ func (c *Chunk) Render() {
return
}
c.Bind()
c.updateRender()
c.Bind()
gl.DrawArrays(gl.TRIANGLES, 0, int32(c.vbolen))
}
// AppendVertex appends the chunk's global vertex into the array.
func (c *Chunk) AppendVertex(arr []Vertex) []Vertex {
func (c *Chunk) RenderWater() {
if !c.checkView(io.ViewPos, io.ViewDir) || !c.checkView(io.RenderPos, io.RenderDir) {
return
}
c.updateRender()
c.BindWater()
gl.DrawArrays(gl.TRIANGLES, 0, int32(c.water.vbolen))
}
// appendVertex appends the chunk's global vertex into the array.
func (c *Chunk) appendVertex(arr, arrwater []Vertex) (fin, finwater []Vertex) {
off := itype.Vec3i{
c.X * ChunkSizeX,
0,
@ -151,14 +187,14 @@ func (c *Chunk) AppendVertex(arr []Vertex) []Vertex {
arr = c.appendFace(itype.ZPlus, itype.Vec3i{i, j, k}, app.Name+"_z+.png", arr)
arr = c.appendFace(itype.ZMinus, itype.Vec3i{i, j, k}, app.Name+"_z-.png", arr)
case CustomRendering:
arr = app.CustomRenderAppend(off.Addv(i, j, k), int(c.Aux[i][j][k]), nil, c.world, arr)
arr, arrwater = app.CustomRenderAppend(off.Addv(i, j, k), int(c.Aux[i][j][k]), nil, c.world, arr, arrwater)
}
}
}
}
return arr
return arr, arrwater
}
func (c *Chunk) appendFace(face itype.Direction, pos itype.Vec3i, texname string, arr []Vertex) []Vertex {

View File

@ -145,3 +145,11 @@ func (w *World) Render() {
c.Render()
}
}
// RenderWater calls DrawArrays drawing the semi-transparant layer of the world.
func (w *World) RenderWater() {
for _, c := range w.Chunks {
c.RenderWater()
}
}