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 <algorithm>
#include <cassert>
namespace oglplus {
class Instances
{
private:
static std::vector<Vec3f> make_positions(void)
{
std::vector<Vec3f> pos_data;
pos_data.reserve(17);
pos_data.push_back(
Vec3f( 10.0f, 0.0f, 70.0f));
pos_data.push_back(
Vec3f( 60.0f, 0.0f, 60.0f));
pos_data.push_back(
Vec3f( 95.0f, 0.0f, 0.0f));
pos_data.push_back(
Vec3f( 60.0f, 0.0f,-60.0f));
pos_data.push_back(
Vec3f( 10.0f, 0.0f,-70.0f));
pos_data.push_back(
Vec3f(-30.0f, 20.0f,-70.0f));
pos_data.push_back(
Vec3f(-70.0f, 40.0f,-70.0f));
pos_data.push_back(
Vec3f(-90.0f, 40.0f,-30.0f));
pos_data.push_back(
Vec3f(-30.0f, 40.0f, 10.0f));
pos_data.push_back(
Vec3f( 40.0f, 40.0f, 10.0f));
pos_data.push_back(
Vec3f( 90.0f, 0.0f, 0.0f));
pos_data.push_back(
Vec3f( 50.0f,-40.0f,-10.0f));
pos_data.push_back(
Vec3f( 0.0f,-40.0f,-10.0f));
pos_data.push_back(
Vec3f(-50.0f,-40.0f,-10.0f));
pos_data.push_back(
Vec3f(-90.0f,-40.0f, 30.0f));
pos_data.push_back(
Vec3f(-70.0f,-40.0f, 70.0f));
pos_data.push_back(
Vec3f(-30.0f,-20.0f, 70.0f));
return pos_data;
}
CubicBezierLoop<Vec3f, double> path_pos;
static std::vector<Vec3f> make_normals(void)
{
std::vector<Vec3f> nml_data;
nml_data.reserve(17);
nml_data.push_back(
Vec3f( 0.0f, 1.0f, 0.0f));
nml_data.push_back(
Vec3f( 0.0f, 1.0f, -1.0f));
nml_data.push_back(
Vec3f( -1.0f, 0.0f, 0.0f));
nml_data.push_back(
Vec3f( 0.0f, 1.0f, 2.0f));
nml_data.push_back(
Vec3f( 0.0f, 1.0f, 1.0f));
nml_data.push_back(
Vec3f( 1.0f, 1.0f, 0.5f));
nml_data.push_back(
Vec3f( 0.0f, 1.0f, 1.0f));
nml_data.push_back(
Vec3f( 1.0f, 0.0f, 0.0f));
nml_data.push_back(
Vec3f( 0.0f, 1.0f, -1.0f));
nml_data.push_back(
Vec3f( 0.0f, 1.0f, 0.0f));
nml_data.push_back(
Vec3f( 1.0f, 0.0f, 0.0f));
nml_data.push_back(
Vec3f( 0.0f, -1.0f, 1.0f));
nml_data.push_back(
Vec3f( 0.0f, 0.0f, 1.0f));
nml_data.push_back(
Vec3f( 0.0f, 1.0f, 1.0f));
nml_data.push_back(
Vec3f( 1.0f, 0.0f, 0.0f));
nml_data.push_back(
Vec3f( 0.0f, 1.0f, -1.0f));
nml_data.push_back(
Vec3f( -1.0f, 1.0f, 0.0f));
for(auto i=nml_data.begin(),e=nml_data.end(); i!=e; ++i)
*i = Normalized(*i);
return nml_data;
}
CubicBezierLoop<Vec3f, double> path_nml;
GLuint count;
public:
Instances(GLuint n = 256)
: path_pos(make_positions(), 0.25)
, path_nml(make_normals(), 0.25)
{
count = 2*n;
std::vector<GLfloat> matrix_data(count*16);
auto p = matrix_data.begin();
double step = 1.0/n;
for(GLuint i=0; i!=n; ++i)
{
Vec3f pos = path_pos.Position(i*step);
path_pos.Position((i+1)*step)-
path_pos.Position((i-1)*step)
);
Vec3f tmp = path_nml.Position(i*step);
Dot(tmp, tgt) != 0.0?
tmp-tgt*Dot(tmp, tgt):
tmp
);
Vec3f btg = Cross(nml, tgt);
for(GLuint j=0; j!=2; ++j)
{
const GLfloat s[2] = {-3.0f, 3.0f};
);
p = std::copy(
Data(matrix),
Data(matrix)+
Size(matrix),
p
);
}
}
assert(p == matrix_data.end());
matrix_buffer.Bind(Buffer::Target::Uniform);
Buffer::Data(
Buffer::Target::Uniform,
matrix_data,
);
}
Vec3f Position(
double t)
const
{
return path_pos.Position(t);
}
Vec3f Normal(
double t)
const
{
return path_nml.Position(t);
}
Mat4f MakeMatrix(
double t,
double dt)
const
{
Vec3f tgt = Normalized(Position(t+dt)-Position(t-dt));
Vec3f tmp = Normal(t+dt);
float dtt = Dot(tmp, tgt);
Vec3f nml = Normalized(dtt != 0.0 ? tmp-tgt*dtt : tmp);
Vec3f btg = Cross(nml, tgt);
));
}
GLuint Count(void) const
{
return count;
}
};
{
private:
{
vs.Source(
"#version 330\n"
"uniform mat4 ProjectionMatrix, CameraMatrix, SingleModelMatrix;"
"uniform uint SingleModel;"
"layout (std140) uniform ModelBlock {"
" mat4 ModelMatrices[512];"
"};"
"const vec3 LightPos = vec3(0.0, 0.0, 0.0);"
"in vec4 Position;"
"in vec3 Normal;"
"in vec2 TexCoord;"
"out vec3 vertLightDir;"
"out vec3 vertNormal;"
"out vec3 vertColor;"
"out vec2 vertTexCoord;"
"void main(void)"
"{"
" mat4 ModelMatrix = "
" SingleModel!=0u?"
" SingleModelMatrix:"
" ModelMatrices[gl_InstanceID];"
" gl_Position = ModelMatrix * Position;"
" vertLightDir = normalize(LightPos - gl_Position.xyz);"
" vertNormal = mat3(ModelMatrix)*Normal;"
" vertTexCoord = SingleModel!=0u ? Position.xz : TexCoord;"
" vertColor = abs(normalize((ModelMatrix*Position).yxz));"
" gl_Position = ProjectionMatrix * CameraMatrix * gl_Position;"
"}"
).Compile();
fs.Source(
"#version 330\n"
"uniform sampler2D CheckerTex;"
"in vec3 vertLightDir;"
"in vec3 vertNormal;"
"in vec3 vertColor;"
"in vec2 vertTexCoord;"
"out vec3 fragColor;"
"void main(void)"
"{"
" float c = texture(CheckerTex, vertTexCoord).r;"
" float l = pow(max(dot(vertLightDir, vertNormal)+0.1,0.0)*1.6,2.0);"
" fragColor = mix("
" vec3(0, 0, 0),"
" vertColor,"
" mix(c, 1.0, 0.2)*(l+0.3)"
" );"
"}"
).Compile();
prog.AttachShader(vs).AttachShader(fs).Link().Use();
return prog;
}
Program&
self(void) {
return *
this; }
public:
Uniform<Mat4f> projection_matrix, camera_matrix, model_matrix;
Uniform<GLuint> single_model;
UniformSampler checker_tex;
DrawProgram(void)
, projection_matrix(self(), "ProjectionMatrix")
, camera_matrix(self(), "CameraMatrix")
, model_matrix(self(), "SingleModelMatrix")
, single_model(self(), "SingleModel")
, checker_tex(self(), "CheckerTex")
, model_block(self(), "ModelBlock")
{ }
};
class ArrowShape
: public ResourceFile
, public shapes::ObjMesh
{
private:
public:
ArrowShape(void)
: ResourceFile("models", "arrow_z", ".obj")
, shapes::ObjMesh(stream(), LoadingOptions(
false).
Normals())
{ }
};
{
private:
{
vs.Source(
"#version 330\n"
"uniform vec2 ScreenSize;"
"in vec4 Position;"
"in vec2 TexCoord;"
"out vec2 vertTexCoord;"
"void main(void)"
"{"
" gl_Position = Position;"
" vertTexCoord = ScreenSize*TexCoord;"
"}"
).Compile();
fs.Source(
"#version 330\n"
"uniform sampler2DRect CurrentFrame;"
"uniform sampler2DRect PreviousFrames;"
"uniform float Splitter;"
"in vec2 vertTexCoord;"
"out vec3 fragColor;"
"vec3 sharp(void)"
"{"
" return texture(CurrentFrame, vertTexCoord).rgb;"
"}"
"vec3 blurred(void)"
"{"
" vec3 prev = vec3(0, 0, 0);"
" const vec2 otc[9] = vec2[9]("
" vec2(-1,-1),"
" vec2( 0,-1),"
" vec2( 1,-1),"
" vec2(-1, 0),"
" vec2( 0, 0),"
" vec2( 1, 0),"
" vec2(-1, 1),"
" vec2( 0, 1),"
" vec2( 1, 1) "
" );"
" const float is = 1.0/9.0;"
" for(int s=0; s!=9; ++s)"
" {"
" vec2 tc = vertTexCoord+otc[s]*2;"
" prev += texture(PreviousFrames, tc).rgb * is;"
" }"
" vec3 curr = texture(CurrentFrame, vertTexCoord).rgb;"
" float a = curr.x+curr.y+curr.z;"
" return mix(curr, prev, 0.95-0.4*a);"
"}"
"void main(void)"
"{"
" if(gl_FragCoord.x < Splitter-1)"
" fragColor = sharp();"
" else if(gl_FragCoord.x > Splitter+1)"
" fragColor = blurred();"
" else fragColor = vec3(1.0, 1.0, 1.0);"
"}"
).Compile();
prog.AttachShader(vs).AttachShader(fs).Link().Use();
return prog;
}
Program&
self(void) {
return *
this; }
public:
Uniform<Vec2f> screen_size;
UniformSampler current_frame, previous_frames;
Uniform<GLfloat> splitter;
BlurProgram(void)
, screen_size(self(), "ScreenSize")
, current_frame(self(), "CurrentFrame")
, previous_frames(self(), "PreviousFrames")
, splitter(self(), "Splitter")
{ }
};
class MotionBlurBuffers
{
private:
Array<Texture> tex;
GLuint width, height;
public:
MotionBlurBuffers(void)
: tex(2)
{
for(GLuint t=0; t!=2; ++t)
{
Texture::Active(t);
Texture::Target tex_tgt = Texture::Target::Rectangle;
tex[t].Bind(tex_tgt);
}
Framebuffer::Target fbo_tgt = Framebuffer::Target::Draw;
fbo.Bind(fbo_tgt);
Framebuffer::AttachTexture(
fbo_tgt,
tex[0],
0
);
rbo.Bind(rbo_tgt);
Framebuffer::AttachRenderbuffer(
fbo_tgt,
rbo
);
}
void Resize(GLuint new_width, GLuint new_height)
{
width = new_width;
height = new_height;
for(GLuint t=0; t!=2; ++t)
{
Texture::Active(t);
Texture::Target tex_tgt = Texture::Target::Rectangle;
tex[t].Bind(tex_tgt);
tex_tgt,
0,
width, height,
0,
nullptr
);
}
rbo.Bind(rbo_tgt);
Renderbuffer::Storage(
rbo_tgt,
width,
height
);
}
void BindFBO(void)
{
fbo.Bind(Framebuffer::Target::Draw);
assert(Framebuffer::IsComplete(Framebuffer::Target::Draw));
}
void Accumulate(void)
{
Texture::Active(1);
tex[1].Bind(Texture::Target::Rectangle);
Texture::CopyImage2D(
Texture::Target::Rectangle,
0,
0, 0,
width, height,
0
);
}
};
{
public:
CheckerTex(TextureUnitSelector tex_unit)
{
Active(tex_unit);
Bind(tex_tgt);
Image2D(tex_tgt, images::CheckerRedBlack(256, 256, 8, 8));
GenerateMipmap(tex_tgt);
}
};
class MotionBlurExample : public Example
{
private:
Context gl;
DrawProgram draw_prog;
Instances instances;
shapes::ShapeWrapper cube;
shapes::ShapeWrapper arrow;
BlurProgram blur_prog;
shapes::ShapeWrapper screen;
CheckerTex checker_tex;
MotionBlurBuffers blur_buffers;
GLuint screen_width;
public:
MotionBlurExample(void)
: gl()
, instances()
, cube(
List(
"Position")(
"Normal")(
"TexCoord").Get(), shapes::Cube(), draw_prog)
, arrow(
List(
"Position")(
"Normal").Get(), ArrowShape(), draw_prog)
, checker_tex(2)
{
draw_prog.Use();
draw_prog.checker_tex.Set(2);
draw_prog.model_block.Binding(0);
instances.matrix_buffer.BindBaseUniform(0);
blur_prog.Use();
blur_prog.current_frame.Set(0);
blur_prog.previous_frames.Set(1);
gl.ClearColor(0.2f, 0.2f, 0.2f, 0.0f);
gl.ClearDepth(1.0f);
}
void Reshape(GLuint width, GLuint height)
{
screen_width = width;
gl.Viewport(width, height);
blur_buffers.Resize(width, height);
auto projection =
Degrees(60),
double(width)/height,
1, 300
);
draw_prog.Use();
draw_prog.projection_matrix.Set(projection);
blur_prog.Use();
blur_prog.screen_size.Set(width, height);
}
void Render(ExampleClock& clock)
{
int samples = 8;
double frame_time = clock.Now().Seconds();
double interval = clock.Interval().Seconds();
double step = interval/samples;
for(int s=0; s!=samples; ++s)
{
double time = frame_time + s*step;
blur_buffers.BindFBO();
gl.Clear().ColorBuffer().DepthBuffer();
double pos = time/20.0;
draw_prog.Use();
draw_prog.camera_matrix.Set(
instances.Position(pos-0.03+
SineWave(time/7.0)*0.01),
instances.Position(pos+0.02+
SineWave(time/11.0)*0.01),
instances.Normal(pos-0.02+
SineWave(time/9.0)*0.02)
)
);
draw_prog.single_model.Set(0);
cube.Use();
cube.Draw(instances.Count());
draw_prog.single_model.Set(1);
arrow.Use();
draw_prog.model_matrix.Set(
instances.MakeMatrix(
0.001
)
);
arrow.Draw();
draw_prog.model_matrix.Set(
instances.MakeMatrix(
0.001
)
);
arrow.Draw();
blur_prog.Use();
screen.Use();
screen.Draw();
blur_buffers.Accumulate();
}
blur_prog.splitter.Set((
SineWave(frame_time / 20.0)*0.5+0.5)*screen_width);
}
{
return time < 90.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 MotionBlurExample);
}
}