/
shadowfbo.go
executable file
·147 lines (123 loc) · 4.32 KB
/
shadowfbo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package lux
import (
"errors"
"github.com/go-gl/gl/v3.3-core/gl"
glm "github.com/go-gl/mathgl/mgl32"
gl2 "github.com/luxengine/gl"
)
//ShadowFBO is the structure to hold all the resources required to render shadow maps.
type ShadowFBO struct {
framebuffer gl2.Framebuffer
texture gl2.Texture2D
projection, view, vp glm.Mat4
program gl2.Program
mvpUni gl2.UniformLocation
width, height int32
}
//NewShadowFBO will create a new FBO, a new depth texture and the program needed to generate shadow map
//currently not optimized, we should probably reuse the FBO and absolutely reuse the program.
func NewShadowFBO(width, height int32) (*ShadowFBO, error) {
sfbo := ShadowFBO{}
fbo := gl2.GenFramebuffer()
sfbo.width, sfbo.height = width, height
sfbo.framebuffer = fbo
fbo.Bind(gl2.FRAMEBUFFER)
defer fbo.Unbind(gl2.FRAMEBUFFER)
shadowtex := gl2.GenTexture2D()
sfbo.texture = shadowtex
shadowtex.Bind()
defer shadowtex.Unbind()
shadowtex.TexParameteriv(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
shadowtex.TexParameteriv(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
shadowtex.TexParameteriv(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
shadowtex.TexParameteriv(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
shadowtex.TexParameteriv(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE)
shadowtex.TexParameteriv(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_FUNC, gl.LEQUAL)
shadowtex.TexImage2D(0, gl.DEPTH_COMPONENT16, width, height, 0, gl.DEPTH_COMPONENT, gl.FLOAT, nil)
fbo.Texture(gl2.FRAMEBUFFER, gl2.DEPTH_ATTACHMENT, shadowtex, 0 /*level*/)
fbo.DrawBuffer(gl2.NONE)
fbo.ReadBuffer(gl2.NONE)
if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE {
return &sfbo, errors.New("framebuffer incomplete")
}
vs, err := CompileShader(_shadowFboVertexShader, gl2.VERTEX_SHADER)
if err != nil {
return &sfbo, err
}
defer vs.Delete()
fs, err := CompileShader(_shadowFboFragmentShader, gl2.FRAGMENT_SHADER)
if err != nil {
return &sfbo, err
}
defer fs.Delete()
prog, err := NewProgram(vs, fs)
if err != nil {
return &sfbo, err
}
sfbo.program = prog
prog.Use()
sfbo.mvpUni = prog.GetUniformLocation("mvp")
return &sfbo, nil
}
//SetOrtho sets the projection matrix to be used.
func (sfbo *ShadowFBO) SetOrtho(left, right, bottom, top, near, far float32) {
sfbo.projection = glm.Ortho(left, right, bottom, top, near, far)
}
//LookAt sets the view matrix to look at (tx,ty,tz) from (ex,ey,ez), the up direction is always (0,1,0).
func (sfbo *ShadowFBO) LookAt(ex, ey, ez, tx, ty, tz float32) {
sfbo.view = glm.LookAt(ex, ey, ez, tx, ty, tz, 0, 1, 0)
}
//BindForDrawing binds this fbo, change face culling for back face, start using the shadow program, calculate projection and clears the texture.
func (sfbo *ShadowFBO) BindForDrawing() {
sfbo.framebuffer.Bind(gl2.FRAMEBUFFER)
sfbo.program.Use()
sfbo.vp = sfbo.projection.Mul4(sfbo.view)
gl.Clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT)
ViewportChange(sfbo.width, sfbo.height)
gl.CullFace(gl.FRONT)
}
//Unbind return cull face to front and unbind this fbo.
func (sfbo *ShadowFBO) Unbind() {
sfbo.framebuffer.Unbind(gl2.FRAMEBUFFER)
gl.CullFace(gl.BACK)
}
//Render takes a mesh and a transform and render them, adding them to the depth texture data.
func (sfbo *ShadowFBO) Render(mesh Mesh, transform *Transform) {
mvpmat := sfbo.vp.Mul4(transform.Mat4())
sfbo.mvpUni.UniformMatrix4fv(1, false, &mvpmat[0])
mesh.Bind()
mesh.DrawCall()
}
//ShadowMap return the depth texture.
func (sfbo *ShadowFBO) ShadowMap() gl2.Texture2D {
return sfbo.texture
}
//ShadowMat return the 4x4 matric that represent world-to-screen transform used to check pixel occlusion.
func (sfbo *ShadowFBO) ShadowMat() glm.Mat4 {
return depthscaling.Mul4(sfbo.vp)
}
//Delete will clean up all the resources allocated to this FBO.
func (sfbo *ShadowFBO) Delete() {
sfbo.texture.Delete()
sfbo.framebuffer.Delete()
sfbo.program.Delete()
}
var _shadowFboVertexShader = `
#version 330 core
uniform mat4 mvp;
layout(location = 0) in vec3 position;
void main(){
gl_Position = mvp * vec4(position,1);
}
` + "\x00"
var _shadowFboFragmentShader = `
#version 330 core
layout(location=0) out float x;
void main(){
x=1;
}
` + "\x00"
var depthscaling = glm.Mat4{0.5, 0, 0, 0,
0, 0.5, 0, 0,
0, 0, 0.5, 0,
0.5, 0.5, 0.5, 1}