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 <cstdlib>
#include <ctime>
namespace oglplus {
class DrawProgram
{
private:
{
Program prog(ObjectDesc(
"Draw program"));
ObjectDesc("Draw vertex"),
"uniform vec3 LightPosition;"
"uniform mat4 ModelMatrix;"
"in vec4 Position;"
"in vec3 Normal;"
"in vec3 Tangent;"
"in vec2 TexCoord;"
"out vec3 vertNormal;"
"out vec3 vertTangent;"
"out vec3 vertBitangent;"
"out vec3 vertLightDir;"
"out vec2 vertTexCoord;"
"void main(void)"
"{"
" gl_Position = "
" ModelMatrix *"
" Position;"
" vertLightDir = LightPosition - gl_Position.xyz;"
" vertNormal = mat3(ModelMatrix)*Normal;"
" vertTangent = mat3(ModelMatrix)*Tangent;"
" vertBitangent = cross(vertNormal, vertTangent);"
" vertTexCoord = TexCoord;"
"}")
));
ObjectDesc("Draw geometry"),
"layout(triangles) in;"
"layout(triangle_strip, max_vertices = 6) out;"
"uniform mat4 CameraMatrix[2];"
"uniform vec3 CameraPosition[2];"
"uniform vec3 Color[2];"
"in vec3 vertNormal[];"
"in vec3 vertTangent[];"
"in vec3 vertBitangent[];"
"in vec3 vertLightDir[];"
"in vec2 vertTexCoord[];"
"out vec3 geomNormal;"
"out vec3 geomTangent;"
"out vec3 geomBitangent;"
"out vec3 geomLightDir;"
"out vec3 geomViewDir;"
"out vec3 geomColor;"
"out vec2 geomTexCoord;"
"void main(void)"
"{"
" for(int l=0; l!=2; ++l)"
" {"
" gl_Layer = l;"
" geomColor = Color[l];"
" for(int v=0; v!=3; ++v)"
" {"
" geomNormal = vertNormal[v];"
" geomTangent = vertTangent[v];"
" geomBitangent = vertBitangent[v];"
" geomTexCoord = vertTexCoord[v];"
" geomLightDir = vertLightDir[v];"
" geomViewDir = "
" CameraPosition[l] - "
" gl_in[v].gl_Position.xyz;"
" gl_Position = "
" CameraMatrix[l] *"
" gl_in[v].gl_Position;"
" EmitVertex();"
" }"
" EndPrimitive();"
" }"
"}")
));
ObjectDesc("Draw fragment"),
"uniform sampler2D MetalTexture;"
"in vec3 geomNormal;"
"in vec3 geomTangent;"
"in vec3 geomBitangent;"
"in vec3 geomLightDir;"
"in vec3 geomViewDir;"
"in vec3 geomColor;"
"in vec2 geomTexCoord;"
"out vec3 fragColor;"
"void main(void)"
"{"
" vec2 TexCoord = vec2(geomTexCoord.s*16, geomTexCoord.t*4);"
" vec3 t = texture(MetalTexture, TexCoord).rgb;"
" vec3 Normal = normalize("
" 2*geomNormal + "
" (t.r - 0.5)*geomTangent + "
" (t.g - 0.5)*geomBitangent"
" );"
" vec3 LightRefl = reflect("
" -normalize(geomLightDir),"
" Normal"
" );"
" float Diffuse = max(dot("
" normalize(Normal), "
" normalize(geomLightDir)"
" ), 0.0);"
" float Specular = pow(max(dot("
" normalize(LightRefl),"
" normalize(geomViewDir)"
" )+0.1, 0.0), 8+t.b*24);"
" vec3 LightColor = vec3(1.0, 1.0, 1.0);"
" vec3 ShapeColor = mix(geomColor, LightColor, t.b);"
" fragColor = "
" ShapeColor*(Diffuse+0.4) + "
" LightColor*Specular*pow(0.6+t.b*0.8, 4.0);"
"}")
));
prog.Link();
return prog;
}
const Program&
self(void)
const {
return *
this; }
public:
ProgramUniform<Mat4f> camera_matrix_0, camera_matrix_1, model_matrix;
ProgramUniform<Vec3f> camera_position_0, camera_position_1;
DrawProgram(void)
, camera_matrix_0(self(), "CameraMatrix[0]")
, camera_matrix_1(self(), "CameraMatrix[1]")
, model_matrix(self(), "ModelMatrix")
, camera_position_0(self(), "CameraPosition[0]")
, camera_position_1(self(), "CameraPosition[1]")
{ }
};
class MetalTexture
{
public:
MetalTexture(TextureUnitSelector unit)
{
Texture::Active(unit);
oglplus::Context::Bound<Texture>(Texture::Target::_2D, *this)
.
Image2D(images::BrushedMetalUByte(512, 512, 5120, -32, 32, 16, 32))
.GenerateMipmap()
}
};
class NoiseTexture
{
public:
NoiseTexture(TextureUnitSelector unit)
{
Texture::Active(unit);
oglplus::Context::Bound<Texture>(Texture::Target::_2D, *this)
.
Image2D(images::RandomRedUByte(512, 512))
}
};
class DrawFramebuffer
{
private:
public:
DrawFramebuffer(TextureUnitSelector color_unit, TextureUnitSelector depth_unit)
{
Texture::Active(depth_unit);
Texture::Active(color_unit);
Resize(256, 256);
}
void Resize(GLuint width, GLuint height)
{
if(width < height) width = height;
if(height < width) height = width;
0,
width, height, 2,
0,
nullptr
);
0,
width, height, 2,
0,
nullptr
);
oglplus::Context::Bound<Framebuffer>(Framebuffer::Target::Draw, *this)
.Complete();
}
};
class ClearProgram
{
private:
{
ObjectDesc("Clear vertex"),
"in vec4 Position;"
"void main(void)"
"{"
" gl_Position = Position;"
"}")
));
ObjectDesc("Clear geometry"),
"layout(triangles) in;"
"layout(triangle_strip, max_vertices = 6) out;"
"uniform vec3 Color1[2], Color2[2];"
"uniform vec2 Origin[2];"
"out vec3 geomColor1, geomColor2;"
"out vec2 geomPosition, geomOrigin;"
"flat out int geomLayer;"
"void main(void)"
"{"
" for(gl_Layer=0; gl_Layer!=2; ++gl_Layer)"
" {"
" geomColor1 = Color1[gl_Layer];"
" geomColor2 = Color2[gl_Layer];"
" geomOrigin = Origin[gl_Layer];"
" geomLayer = gl_Layer;"
" for(int v=0; v!=3; ++v)"
" {"
" gl_Position = gl_in[v].gl_Position;"
" geomPosition = gl_Position.xy;"
" EmitVertex();"
" }"
" EndPrimitive();"
" }"
"}")
));
ObjectDesc("Clear fragment"),
"in vec3 geomColor1, geomColor2;"
"in vec2 geomPosition, geomOrigin;"
"flat in int geomLayer;"
"out vec3 fragColor;"
"void main(void)"
"{"
" vec2 v = geomPosition - geomOrigin;"
" float l = length(v);"
" float a = atan(v.y, v.x)/3.1415;"
" float d = 32-geomLayer*15;"
" if(l < 0.01)"
" fragColor = geomColor1;"
" else if(int(d*(1.0 + a)) % 2 == 0)"
" fragColor = geomColor1 * 0.9 + l*0.1;"
" else"
" fragColor = geomColor2 * 0.8 + pow(l,1.4)*0.2;"
"}")
));
prog.Link();
return prog;
}
const Program&
self(void)
const {
return *
this; }
public:
ProgramUniform<Vec2f> origin_0, origin_1;
ClearProgram(void)
, origin_0(self(), "Origin[0]")
, origin_1(self(), "Origin[1]")
{ }
};
class TransitionProgram
{
private:
{
ObjectDesc("Transition vertex"),
"in vec4 Position;"
"out vec2 vertPosition, vertTexCoord;"
"void main(void)"
"{"
" gl_Position = Position;"
" vertPosition = Position.xy;"
" vertTexCoord = vec2("
" (Position.x*0.5 + 0.5),"
" (Position.y*0.5 + 0.5) "
" );"
"}")
));
ObjectDesc("Transition fragment"),
"uniform sampler2DArray FrameTex;"
"uniform sampler2D NoiseTex;"
"uniform float MixFactor;"
"uniform bool Direction;"
"subroutine vec4 TransitionFunc(void);"
"subroutine uniform TransitionFunc Transition;"
"in vec2 vertPosition, vertTexCoord;"
"out vec4 fragColor;"
"int Frame0(void) { return Direction?1:0; }"
"int Frame1(void) { return Direction?0:1; }"
"float Factor(void) { return Direction?1.0-MixFactor:MixFactor; }"
"vec4 Texel0(void)"
"{"
" return texelFetch(FrameTex, ivec3(gl_FragCoord.xy, Frame0()), 0);"
"}"
"vec4 Texel1(void)"
"{"
" return texelFetch(FrameTex, ivec3(gl_FragCoord.xy, Frame1()), 0);"
"}"
"vec4 MixTexels(float factor)"
"{"
" return mix(Texel0(), Texel1(), factor);"
"}"
"subroutine(TransitionFunc)"
"vec4 Vertical(void)"
"{"
" return MixTexels((vertTexCoord.y > Factor())?0.0:1.0);"
"}"
"subroutine(TransitionFunc)"
"vec4 Horizontal(void)"
"{"
" return MixTexels((vertTexCoord.x > Factor())?0.0:1.0);"
"}"
"subroutine(TransitionFunc)"
"vec4 Spiral(void)"
"{"
" vec2 v = vertPosition.xy*31.0;"
" float l = sqrt(length(v));"
" float t = atan(v.y, v.x)/3.1415;"
" return MixTexels((fract(l+t) > Factor())?0.0:1.0);"
"}"
"subroutine(TransitionFunc)"
"vec4 Rings(void)"
"{"
" vec2 v = vertPosition.xy*12.0;"
" float l = length(v);"
" return MixTexels((fract(l) > Factor())?0.0:1.0);"
"}"
"subroutine(TransitionFunc)"
"vec4 SweepFade(void)"
"{"
" vec2 v = vertTexCoord;"
" float l = length(v);"
" float a = atan(v.y, v.x)/3.1415;"
" return MixTexels(clamp(2*Factor() - a, 0.0, 1.0));"
"}"
"subroutine(TransitionFunc)"
"vec4 CenterFan(void)"
"{"
" vec2 v = vertPosition;"
" float l = length(v);"
" float a = atan(-v.y, v.x)/3.1415;"
" return MixTexels((fract(9*a) > Factor())?0.0:1.0);"
"}"
"subroutine(TransitionFunc)"
"vec4 CornerFan(void)"
"{"
" vec2 v = vertTexCoord;"
" float l = length(v);"
" float a = atan(-v.y, v.x)/3.1415;"
" return MixTexels((fract(15*a) > Factor())?0.0:1.0);"
"}"
"subroutine(TransitionFunc)"
"vec4 Rectangle(void)"
"{"
" float x = (abs(vertPosition.x)<Factor())?1.0:0.0;"
" float y = (abs(vertPosition.y)<Factor())?1.0:0.0;"
" return MixTexels(x*y);"
"}"
"subroutine(TransitionFunc)"
"vec4 EllipseFade(void)"
"{"
" float t = length(vertPosition)/sqrt(2.0);"
" return MixTexels(clamp(2*Factor()-t, 0.0, 1.0));"
"}"
"subroutine(TransitionFunc)"
"vec4 Ellipse(void)"
"{"
" float t = length(vertPosition)/sqrt(2.0);"
" return MixTexels((Factor()>t)?1.0:0.0);"
"}"
"subroutine(TransitionFunc)"
"vec4 Circles(void)"
"{"
" int a = 16;"
" int s = 2*a;"
" float r = sqrt(2.0)*a;"
" vec2 fc = gl_FragCoord.xy;"
" float cv = length(vec2(int(fc.x) % s - a, int(fc.y) % s - a)/r);"
" float tc = vertTexCoord.x * vertTexCoord.y;"
" return MixTexels(clamp(3*Factor()-tc-cv, 0.0, 1.0));"
"}"
"subroutine(TransitionFunc)"
"vec4 Blend(void)"
"{"
" return MixTexels(Factor());"
"}"
"subroutine(TransitionFunc)"
"vec4 DiagDissolve(void)"
"{"
" float t = texture(NoiseTex, vertTexCoord).r;"
" float tc = vertTexCoord.x*vertTexCoord.y;"
" return MixTexels(clamp(3*Factor()-tc-t, 0.0, 1.0));"
"}"
"void main(void)"
"{"
" fragColor = Transition();"
"}")
));
prog.Link();
return prog;
}
const Program&
self(void)
const {
return *
this; }
UniformSubroutines frag_subroutines;
std::vector<Subroutine> transitions;
GLfloat prev_mix_factor;
public:
ProgramUniform<GLfloat> mix_factor;
ProgramUniform<GLint> direction;
TransitionProgram(void)
, mix_factor(self(), "MixFactor")
, direction(self(), "Direction")
{
for(auto sr=ActiveSubroutines(stage); !sr.Empty(); sr.Next())
transitions.push_back(
Subroutine(
self(), stage, sr.Front().Name()));
SetMixFactor(0.0f);
}
void NextTransition(void)
{
std::size_t next = std::rand() % transitions.size();
frag_subroutines.Assign(frag_transition, transitions[next]);
direction.Set(std::rand() % 2);
}
void SetMixFactor(GLfloat factor)
{
const GLfloat margin = 0.2f;
factor -= margin;
factor /= (1.0f - 2.0f*margin);
if(factor <= 0.0f)
{
if(prev_mix_factor > 0.0)
NextTransition();
factor = 0.0f;
}
else if(factor >= 1.0f)
{
if(prev_mix_factor < 1.0)
NextTransition();
factor = 1.0f;
}
mix_factor = factor;
prev_mix_factor = factor;
frag_subroutines.Apply();
}
};
class Screen
{
private:
Context gl;
public:
{
vao.Bind();
corners.Bind(Buffer::Target::Array);
{
GLfloat screen_verts[8] = {
-1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, -1.0f,
1.0f, 1.0f
};
Buffer::Data(Buffer::Target::Array, 8, screen_verts);
VertexArrayAttrib attr(
VertexArrayAttrib::GetCommonLocation(
MakeGroup(prog1, prog2),
"Position"
)
);
attr.Enable();
}
}
{
vao.Bind();
}
};
class TransitionExample : public Example
{
private:
Context gl;
DrawProgram draw_prog;
static std::size_t torus_vert_attr_count(void) { return 4; }
static const GLchar** torus_vert_attr_names(void)
{
static const GLchar* attrs[] = {"Position", "Normal", "Tangent", "TexCoord"};
assert(sizeof(attrs)/sizeof(attrs[0]) == torus_vert_attr_count());
return attrs;
}
shapes::ShapeWrapper torus;
GLint next_free_tex_unit;
const GLint metal_tex_unit;
MetalTexture metal_tex;
const GLint noise_tex_unit;
NoiseTexture noise_tex;
const GLint color_tex_unit, depth_tex_unit;
DrawFramebuffer draw_fbo;
ClearProgram clear_prog;
TransitionProgram trans_prog;
GLuint width, height;
Mat4f projection_0, projection_1;
public:
TransitionExample(void)
: draw_prog()
, torus(
torus_vert_attr_names(),
torus_vert_attr_count(),
shapes::WickerTorus(1.5, 1.0, 0.03, 18, 36),
draw_prog
), next_free_tex_unit(0)
, metal_tex_unit(next_free_tex_unit++)
, metal_tex(metal_tex_unit)
, noise_tex_unit(next_free_tex_unit++)
, noise_tex(noise_tex_unit)
, color_tex_unit(next_free_tex_unit++)
, depth_tex_unit(next_free_tex_unit++)
, draw_fbo(color_tex_unit, depth_tex_unit)
, clear_prog()
, trans_prog()
, screen(clear_prog, trans_prog)
{
ProgramUniformSampler(draw_prog, "MetalTexture").Set(metal_tex_unit);
ProgramUniform<Vec3f>(draw_prog,
"LightPosition").
Set(10.0f, 30.0f, 20.0f);
ProgramUniform<Vec3f>(draw_prog,
"Color[0]").
Set(0.7f, 0.1f, 0.2f);
ProgramUniform<Vec3f>(draw_prog,
"Color[1]").
Set(0.2f, 0.1f, 0.7f);
ProgramUniform<Vec3f>(clear_prog,
"Color1[0]").
Set(0.9f, 0.4f, 0.5f);
ProgramUniform<Vec3f>(clear_prog,
"Color2[0]").
Set(1.0f, 0.5f, 0.6f);
ProgramUniform<Vec3f>(clear_prog,
"Color1[1]").
Set(0.5f, 0.4f, 0.9f);
ProgramUniform<Vec3f>(clear_prog,
"Color2[1]").
Set(0.6f, 0.5f, 1.0f);
ProgramUniformSampler(trans_prog, "FrameTex").Set(color_tex_unit);
ProgramUniformSampler(trans_prog, "NoiseTex").Set(noise_tex_unit);
gl.ClearDepth(1.0f);
}
void Reshape(GLuint vp_width, GLuint vp_height)
{
width = vp_width;
height = vp_height;
draw_fbo.Resize(width, height);
Degrees(70),
float(width)/height,
1, 20
);
Degrees(24),
float(width)/height,
1, 40
);
}
void RenderFrames(double time)
{
target_0,
Degrees(
SineWave(time / 20.0) * 30 + 35)
);
auto cm_0 = projection_0*camera_0;
draw_prog.camera_matrix_0 = cm_0;
draw_prog.camera_position_0 = camera_0.Position();
clear_prog.origin_0 = (cm_0*
Vec4f(origin, 1.0)).xy();
target_1,
12.5,
);
auto cm_1 = projection_1*camera_1;
draw_prog.camera_matrix_1 = cm_1;
draw_prog.camera_position_1 = camera_1.Position();
clear_prog.origin_1 = (cm_1*
Vec4f(origin, 1.0)).xy();
gl.Viewport(width, height);
clear_prog.Use();
screen.Draw();
gl.Clear().DepthBuffer();
draw_prog.model_matrix =
draw_prog.Use();
torus.Use();
torus.Draw();
}
void MergeFrames(double time)
{
gl.Viewport(width, height);
trans_prog.Use();
trans_prog.SetMixFactor(-
CosineWave(time / 6.0)*0.5+0.5);
screen.Draw();
}
{
draw_fbo.Bind(Framebuffer::Target::Draw);
RenderFrames(time);
def_fb.Bind(Framebuffer::Target::Draw);
MergeFrames(time);
}
{
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& )
{
std::srand(std::time(0));
return std::unique_ptr<Example>(new TransitionExample);
}
}