Copyright 2008-2014 Matus Chochlik. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <cmath>
namespace oglplus {
struct MeshInputFile
{
std::ifstream stream;
MeshInputFile(void)
{
OpenResourceFile(stream, "models", "large_fan", ".obj");
}
};
{
public:
CommonVertexShader(void)
{
"#version 330\n"
"uniform vec3 LightPosition;"
"uniform mat4 ProjectionMatrix, CameraMatrix, ModelMatrix, LightMatrix;"
"in vec4 Position;"
"in vec3 Normal;"
"out vec3 vertNormal;"
"out vec3 vertLightDir;"
"out vec4 vertShadowCoord;"
"void main(void)"
"{"
" gl_Position = ModelMatrix * Position;"
" vertLightDir = normalize(LightPosition - gl_Position.xyz);"
" vertNormal = normalize(mat3(ModelMatrix) * Normal);"
" vertShadowCoord = LightMatrix * ModelMatrix * Position;"
" gl_Position = ProjectionMatrix * CameraMatrix * gl_Position;"
"}"
);
Compile();
}
};
class ShadowProgram :
public Program
{
private:
{
fs.Source(
"#version 330\n"
"void main(void) { }"
).Compile();
prog.AttachShader(vs).AttachShader(fs);
prog.Link().Use();
return prog;
}
Program&
self(void) {
return *
this; }
public:
Uniform<Mat4f> projection_matrix, camera_matrix;
, projection_matrix(self(), "ProjectionMatrix")
, camera_matrix(self(), "CameraMatrix")
{
}
};
{
private:
{
fs.Source(
"#version 330\n"
"uniform sampler2DShadow ShadowMap;"
"in vec3 vertNormal;"
"in vec3 vertLightDir;"
"in vec4 vertShadowCoord;"
"out float fragIntensity;"
"void main(void)"
"{"
" vec3 ShadowCoord = (vertShadowCoord.xyz/vertShadowCoord.w)*0.5 + 0.5;"
" float s = 0.0f;"
" if("
" ShadowCoord.x >= 0.0 && "
" ShadowCoord.x <= 1.0 && "
" ShadowCoord.y >= 0.0 && "
" ShadowCoord.y <= 1.0 && "
" ShadowCoord.z <= 1.0"
" ) s = max(texture(ShadowMap, ShadowCoord), 0.05);"
" float l = max(dot(vertNormal, vertLightDir)+0.1, 0.0);"
" fragIntensity = l * s;"
"}"
).Compile();
prog.AttachShader(vs).AttachShader(fs);
prog.Link().Use();
return prog;
}
Program&
self(void) {
return *
this; }
public:
Uniform<Vec3f> light_position;
Uniform<Mat4f> projection_matrix, camera_matrix, model_matrix, light_matrix;
, light_position(self(), "LightPosition")
, projection_matrix(self(), "ProjectionMatrix")
, camera_matrix(self(), "CameraMatrix")
, model_matrix(self(), "ModelMatrix")
, light_matrix(self(), "LightMatrix")
{ }
};
{
private:
{
fs.Source(
"#version 330\n"
"const vec3 LightColor = vec3(0.6, 0.6, 1.0);"
"const vec3 Up = normalize(vec3(0.1, 1.0, 0.1));"
"uniform vec3 LightScreenPos;"
"uniform vec2 ScreenSize;"
"uniform sampler2DRect LightMap;"
"uniform sampler2DShadow ShadowMap;"
"in vec3 vertNormal;"
"in vec3 vertLightDir;"
"in vec4 vertShadowCoord;"
"out vec3 fragColor;"
"void main(void)"
"{"
" vec3 ShadowCoord = (vertShadowCoord.xyz/vertShadowCoord.w)*0.5 + 0.5;"
" float s = 0.0f;"
" if("
" ShadowCoord.x >= 0.0 && "
" ShadowCoord.x <= 1.0 && "
" ShadowCoord.y >= 0.0 && "
" ShadowCoord.y <= 1.0 && "
" ShadowCoord.z <= 1.0"
" ) s = texture(ShadowMap, ShadowCoord);"
" float a = 0.1*(max(dot(vertNormal, Up)+0.1, 0.0)+0.1);"
" float d = max(dot(vertNormal, vertLightDir)+0.1, 0.0)+a;"
" vec2 LMCoord = gl_FragCoord.xy;"
" vec2 LPos = (LightScreenPos.xy*0.5+0.5)*ScreenSize;"
" vec2 Ray = LMCoord - LPos;"
" float Len = length(Ray);"
" int NSampl = int(max(abs(Ray.x), abs(Ray.y)))+1;"
" vec2 RayStep = Ray / NSampl;"
" float r = texture(LightMap, LMCoord).r;"
" NSampl = min(NSampl, int(min(ScreenSize.x, ScreenSize.y)*0.25));"
" for(int s=0; s!=NSampl;++s)"
" {"
" r += texture(LightMap, LPos+RayStep*s).r;"
" }"
" r /= NSampl;"
" r = min(r, 1.0);"
" fragColor = LightColor * (mix(a, d, s) + r);"
"}"
).Compile();
prog.AttachShader(vs).AttachShader(fs);
prog.Link().Use();
return prog;
}
Program&
self(void) {
return *
this; }
public:
Uniform<Vec3f> light_position;
Uniform<Vec3f> light_screen_pos;
Uniform<Vec2f> screen_size;
Uniform<Mat4f> projection_matrix, camera_matrix, model_matrix, light_matrix;
, light_position(self(), "LightPosition")
, light_screen_pos(self(), "LightScreenPos")
, screen_size(self(), "ScreenSize")
, projection_matrix(self(), "ProjectionMatrix")
, camera_matrix(self(), "CameraMatrix")
, model_matrix(self(), "ModelMatrix")
, light_matrix(self(), "LightMatrix")
{ }
};
class LightRayExample : public Example
{
private:
Context gl;
MeshInputFile mesh_input;
shapes::ObjMesh mesh_loader;
shapes::ShapeWrapper meshes;
GLuint fan_index;
CommonVertexShader vert_shader;
MaskProgram mask_prog;
DrawProgram draw_prog;
TextureUnitSelector shadow_tex_unit;
void RenderShadowMap(GLuint size)
{
Texture::Active(shadow_tex_unit);
mask_prog.Use();
Uniform<Mat4f>(mask_prog,
"LightMatrix").
Set(lt_proj*light);
UniformSampler(mask_prog, "ShadowMap").Set(GLuint(shadow_tex_unit));
draw_prog.Use();
Uniform<Mat4f>(draw_prog,
"LightMatrix").
Set(lt_proj*light);
UniformSampler(draw_prog, "ShadowMap").Set(GLuint(shadow_tex_unit));
Texture::Target tex_tgt = Texture::Target::_2D;
shadow_map.Bind(tex_tgt);
tex_tgt,
0,
size, size,
0,
nullptr
);
ShadowProgram shadow_prog(vert_shader);
vao.Bind();
Framebuffer::Target fbo_tgt = Framebuffer::Target::Draw;
fbo.Bind(fbo_tgt);
rbo.Bind(rbo_tgt);
shadow_prog.projection_matrix.Set(lt_proj);
shadow_prog.camera_matrix.Set(light);
gl.Viewport(size, size);
gl.Clear().DepthBuffer();
gl.PolygonOffset(1.0, 1.0);
meshes.Draw();
gl.Finish();
}
TextureUnitSelector light_tex_unit;
void SetupLightMask(void)
{
Texture::Active(light_tex_unit);
Texture::Target tex_tgt = Texture::Target::Rectangle;
light_mask.Bind(tex_tgt);
draw_prog.Use();
UniformSampler(draw_prog, "LightMap").Set(GLuint(light_tex_unit));
Framebuffer::Target fbo_tgt = Framebuffer::Target::Draw;
light_fbo.Bind(fbo_tgt);
light_rbo.Bind(rbo_tgt);
}
void ResizeLightMask(GLuint width, GLuint height)
{
Texture::Active(light_tex_unit);
Texture::Target tex_tgt = Texture::Target::Rectangle;
light_mask.Bind(tex_tgt);
tex_tgt,
0,
width, height,
0,
nullptr
);
light_rbo.Bind(rbo_tgt);
Renderbuffer::Storage(
rbo_tgt,
width,
height
);
}
public:
LightRayExample(void)
: gl()
, mesh_loader(
mesh_input.stream,
shapes::ObjMesh::LoadingOptions(false).Normals()
), meshes(
List(
"Position")(
"Normal").Get(), mesh_loader)
, fan_index(mesh_loader.GetMeshIndex("Fan"))
, light_position(0.0, 0.0, -100.0)
, vert_shader()
, mask_prog(vert_shader)
, mask_vao(meshes.VAOForProgram(mask_prog))
, draw_prog(vert_shader)
, draw_vao(meshes.VAOForProgram(draw_prog))
, shadow_tex_unit(0)
, light_tex_unit(1)
{
mesh_input.stream.close();
gl.ClearDepth(1.0f);
RenderShadowMap(512);
SetupLightMask();
mask_prog.Use();
mask_prog.light_position.Set(light_position);
draw_prog.Use();
draw_prog.light_position.Set(light_position);
gl.ClearColor(1.0f, 1.0f, 1.0f, 0.0f);
}
void Reshape(GLuint width, GLuint height)
{
gl.Viewport(width, height);
projection =
Degrees(70),
double(width)/height,
1, 200
);
mask_prog.Use();
mask_prog.projection_matrix.Set(projection);
draw_prog.Use();
draw_prog.projection_matrix.Set(projection);
draw_prog.screen_size.Set(width, height);
ResizeLightMask(width, height);
}
{
auto camera =
40.0f,
);
Uniform<Mat4f>* model_matrix = nullptr;
GLuint drawing_fan = fan_index;
auto drawing_driver =
[
&model_matrix,
&mm_identity,
&mm_rotation,
&drawing_fan
](GLuint phase) -> bool
{
if(phase == drawing_fan)
model_matrix->Set(mm_rotation);
else model_matrix->Set(mm_identity);
return true;
};
light_fbo.Bind(Framebuffer::Target::Draw);
gl.Clear().ColorBuffer().DepthBuffer();
mask_vao.Bind();
mask_prog.Use();
mask_prog.camera_matrix.Set(camera);
model_matrix = &mask_prog.model_matrix;
meshes.Draw(drawing_driver);
gl.Clear().ColorBuffer().DepthBuffer();
draw_vao.Bind();
draw_prog.Use();
Vec4f lsp = projection * camera *
Vec4f(light_position, 1.0);
draw_prog.light_screen_pos = lsp.xyz()/lsp.w();
draw_prog.camera_matrix.Set(camera);
model_matrix = &draw_prog.model_matrix;
meshes.Draw(drawing_driver);
}
{
return time < 90.0;
}
double ScreenshotTime(void) const
{
return 17.0;
}
};
void setupExample(ExampleParams& ){ }
std::unique_ptr<ExampleThread> makeExampleThread(
Example& ,
unsigned ,
const ExampleParams&
){ return std::unique_ptr<ExampleThread>(); }
std::unique_ptr<Example> makeExample(const ExampleParams& )
{
return std::unique_ptr<Example>(new LightRayExample);
}
}