//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 }
//NewPostProcessFramebuffer creates a new PostProcessFramebuffer and allocated all the ressources. //You do not control the vertex shader but you can give a fragment shader. The fragment shader must have the following uniforms: // -resolution: float vec2, representing the size of the texture // -time: float, glfw time since the begining of the program // -tex: sampler2D, the input texture to this post process pass func NewPostProcessFramebuffer(width, height int32, fragmentSource string) (*PostProcessFramebuffer, error) { ppf := PostProcessFramebuffer{} vs, err := CompileShader(_fullscreenVertexShader, gl2.VERTEX_SHADER) if err != nil { return &ppf, err } defer vs.Delete() fs, err := CompileShader(fragmentSource, gl2.FRAGMENT_SHADER) if err != nil { return &ppf, err } defer fs.Delete() prog, err := NewProgram(vs, fs) if err != nil { return &ppf, err } ppf.Prog = prog prog.Use() res := prog.GetUniformLocation("resolution") ppf.source = prog.GetUniformLocation("tex") res.Uniform2f(float32(width), float32(height)) ppf.time = prog.GetUniformLocation("time") ppf.Fb = gl2.GenFramebuffer() ppf.Tex = GenRGBTexture2D(width, height) ppf.Fb.Bind(gl2.FRAMEBUFFER) defer ppf.Fb.Unbind(gl2.FRAMEBUFFER) ppf.Fb.Texture(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, ppf.Tex, 0) ppf.Fb.DrawBuffers(gl2.COLOR_ATTACHMENT0) if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE { return &ppf, errors.New("framebuffer incomplete") } return &ppf, nil }
//NewGBuffer will create a new geometry buffer and allocate all the resources required func NewGBuffer(width, height int32) (gbuffer GBuffer, err error) { const ( _gbufferVertexShaderSource = `#version 330 uniform mat4 M; uniform mat4 MVP; layout (location=0) in vec3 vert; layout (location=1) in vec2 vertTexCoord; layout (location=2) in vec3 vertNormal; out vec2 fragTexCoord; out vec3 normal; out vec3 world_pos; void main() { normal = vertNormal; fragTexCoord = vertTexCoord; world_pos=(M*vec4(vert,1)).xyz; gl_Position = MVP * vec4(vert, 1); } ` + "\x00" _gbufferFragmentShaderSource = `#version 330 uniform sampler2D diffuse; uniform mat4 N; in vec2 fragTexCoord; in vec3 normal; in vec3 world_pos; layout (location=0) out vec3 outColor; layout (location=1) out vec3 outNormal; layout (location=2) out vec3 outPosition; void main() { outColor = texture(diffuse, fragTexCoord).rgb; outNormal = (N*vec4(normal,1)).xyz; outPosition = world_pos; } ` + "\x00" //shouldnt be there _gbufferAggregateFragmentShader = `#version 330 #define MAX_POINT_LIGHT 8 #define MIN_LUX 0.3 //GBuffer textures uniform sampler2D diffusetex; uniform sampler2D normaltex; uniform sampler2D postex; uniform sampler2D depthtex; //cook uniform float roughnessValue; uniform float F0; uniform float k; //Lights uniform int NUM_POINT_LIGHT; uniform vec3 point_light_pos[MAX_POINT_LIGHT]; uniform vec3 point_light_color[MAX_POINT_LIGHT]; //Shadows uniform sampler2DShadow shadowmap; uniform mat4 shadowmat; //View uniform vec3 cam_pos; in vec2 uv; layout (location=0) out vec4 outColor; void main(){ vec3 normal = normalize(texture(normaltex, uv).xyz); vec3 world_position = texture(postex, uv).xyz; vec4 shadowcoord = shadowmat*vec4(world_position, 1); shadowcoord.z+=0.005; float shadow = texture(shadowmap, shadowcoord.xyz,0); //////cook torrance //material values vec3 lightColor = vec3(0.9,0.1,0.1); vec3 world_pos = texture(postex, uv).xyz; vec3 lightDir = point_light_pos[0]-world_pos; float NdL = max(dot(normal, lightDir), 0); float lux = shadow; if(shadow > 0){ float specular = 0.0; if(NdL > 0.0){ vec3 eyeDir = normalize(cam_pos-world_pos); vec3 halfVec = normalize(lightDir+eyeDir); float NdH = max(0,dot(normal,halfVec)); float NdV = max(0,dot(normal, eyeDir)); float VdH = max(0,dot(eyeDir, halfVec)); float mSqu = roughnessValue*roughnessValue; float NH2 = 2.0*NdH; float geoAtt = min(1.0,min((NH2*NdV)/VdH,(NH2*NdL)/VdH)); float roughness = (1.0 / ( 4.0 * mSqu * pow(NdH, 4.0)))*exp((NdH * NdH - 1.0) / (mSqu * NdH * NdH)); float fresnel = pow(1.0 - VdH, 5.0)*(1.0 - F0)+F0; specular = (fresnel*geoAtt*roughness)/(NdV*NdL*3.14); } lux=NdL * (k + specular * (1.0 - k)); } if(lux < MIN_LUX){ lux=MIN_LUX; } outColor = texture(diffusetex, uv)*lux; } ` + "\x00" ) gbuffer.width, gbuffer.height = width, height fb := gl2.GenFramebuffer() fb.Bind(gl2.FRAMEBUFFER) defer fb.Unbind(gl2.FRAMEBUFFER) gbuffer.framebuffer = fb depthtex := gl2.GenTexture2D() depthtex.Bind() depthtex.MinFilter(gl2.NEAREST) depthtex.MagFilter(gl2.NEAREST) depthtex.WrapS(gl2.CLAMP_TO_EDGE) depthtex.WrapT(gl2.CLAMP_TO_EDGE) depthtex.TexImage2D(0, gl2.DEPTH24_STENCIL8, width, height, 0, gl2.DEPTH_STENCIL, gl2.UNSIGNED_INT_24_8, nil) diffuseTex := gl2.GenTexture2D() diffuseTex.Bind() diffuseTex.MinFilter(gl2.LINEAR) diffuseTex.MagFilter(gl2.LINEAR) diffuseTex.WrapS(gl2.CLAMP_TO_EDGE) diffuseTex.WrapT(gl2.CLAMP_TO_EDGE) diffuseTex.TexImage2D(0, gl2.RGB16F, width, height, 0, gl2.RGBA, gl2.FLOAT, nil) normalTex := gl2.GenTexture2D() normalTex.Bind() normalTex.MinFilter(gl2.LINEAR) normalTex.MagFilter(gl2.LINEAR) normalTex.WrapS(gl2.CLAMP_TO_EDGE) normalTex.WrapT(gl2.CLAMP_TO_EDGE) normalTex.TexImage2D(0, gl2.RGB16F, width, height, 0, gl2.RGB, gl2.FLOAT, nil) positionTex := gl2.GenTexture2D() positionTex.Bind() positionTex.MinFilter(gl2.LINEAR) positionTex.MagFilter(gl2.LINEAR) positionTex.WrapS(gl2.CLAMP_TO_EDGE) positionTex.WrapT(gl2.CLAMP_TO_EDGE) positionTex.TexImage2D(0, gl2.RGB16F, width, height, 0, gl2.RGB, gl2.FLOAT, nil) fb.DrawBuffers(gl2.COLOR_ATTACHMENT0, gl2.COLOR_ATTACHMENT1, gl2.COLOR_ATTACHMENT2) fb.Texture(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, diffuseTex, 0 /*level*/) fb.Texture(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT1, normalTex, 0 /*level*/) fb.Texture(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT2, positionTex, 0 /*level*/) fb.Texture(gl2.FRAMEBUFFER, gl2.DEPTH_STENCIL_ATTACHMENT, depthtex, 0 /*level*/) gbuffer.DiffuseTex = diffuseTex gbuffer.NormalTex = normalTex gbuffer.PositionTex = positionTex gbuffer.DepthTex = depthtex vs, err := CompileShader(_gbufferVertexShaderSource, gl2.VERTEX_SHADER) if err != nil { return } fs, err := CompileShader(_gbufferFragmentShaderSource, gl2.FRAGMENT_SHADER) if err != nil { return } prog, err := NewProgram(vs, fs) if err != nil { return } gbuffer.program = prog prog.Use() defer prog.Unuse() gbuffer.PUni = prog.GetUniformLocation("P") gbuffer.VUni = prog.GetUniformLocation("V") gbuffer.MUni = prog.GetUniformLocation("M") gbuffer.NUni = prog.GetUniformLocation("N") gbuffer.MVPUni = prog.GetUniformLocation("MVP") gbuffer.DiffuseUni = prog.GetUniformLocation("diffuse") //shadow map prog.BindFragDataLocation(0, "") prog.BindFragDataLocation(1, "") prog.BindFragDataLocation(2, "") //Aggregated fb and textures, essentially a special post process effect aggfb := AggregateFB{} gbuffer.AggregateFramebuffer = aggfb avs, err := CompileShader(_fullscreenVertexShader, gl2.VERTEX_SHADER) if err != nil { return } afs, err := CompileShader(_gbufferAggregateFragmentShader, gl2.FRAGMENT_SHADER) if err != nil { return } aprog, err := NewProgram(avs, afs) if err != nil { return } aggfb.program = aprog aggfb.framebuffer = gl2.GenFramebuffer() aggfb.framebuffer.Bind(gl2.FRAMEBUFFER) aggfb.Out = gl2.GenTexture2D() aggfb.Out.Bind() aggfb.Out.MinFilter(gl2.LINEAR) aggfb.Out.MagFilter(gl2.LINEAR) aggfb.Out.WrapS(gl2.CLAMP_TO_EDGE) aggfb.Out.WrapT(gl2.CLAMP_TO_EDGE) aggfb.Out.TexImage2D(0, gl2.RGBA16F, width, height, 0, gl2.RGB, gl2.FLOAT, nil) aggfb.DiffUni = aprog.GetUniformLocation("diffusetex") aggfb.NormalUni = aprog.GetUniformLocation("normaltex") aggfb.PosUni = aprog.GetUniformLocation("postex") aggfb.DepthUni = aprog.GetUniformLocation("depthtex") gbuffer.ShadowMapUni = aprog.GetUniformLocation("shadowmap") gbuffer.ShadowMatUni = aprog.GetUniformLocation("shadowmat") gbuffer.NumPointLightUni = aprog.GetUniformLocation("NUM_POINT_LIGHT") gbuffer.PointLightPosUni = aprog.GetUniformLocation("point_light_pos") gbuffer.PointLightColUni = aprog.GetUniformLocation("point_light_color") gbuffer.CamPosUni = aprog.GetUniformLocation("cam_pos") //test data for cook torrance shader gbuffer.CookRoughnessValue = aprog.GetUniformLocation("roughnessValue") gbuffer.CookF0 = aprog.GetUniformLocation("F0") gbuffer.CookK = aprog.GetUniformLocation("k") aggfb.framebuffer.DrawBuffers(gl2.COLOR_ATTACHMENT0) aggfb.framebuffer.Texture(gl2.FRAMEBUFFER, gl2.COLOR_ATTACHMENT0, aggfb.Out, 0) gbuffer.AggregateFramebuffer = aggfb return }