Skip to content

Instantly share code, notes, and snippets.

@roxlu
Last active January 2, 2016 13:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roxlu/8308562 to your computer and use it in GitHub Desktop.
Save roxlu/8308562 to your computer and use it in GitHub Desktop.
Projective texture mapping; see http://www.reedbeta.com/blog/2012/05/26/quadrilateral-interpolation-part-1/ for more information
#include <swnt/Effects.h>
#include <swnt/effects/Mist.h>
template<class T>
inline bool intersect(const Vec3<T>& p0, const Vec3<T>& p1, const Vec3<T>& p2, const Vec3<T>& p3, Vec3<T>& result) {
Vec3<T> s1 = p1 - p0;
Vec3<T> s2 = p3 - p2;
float s, t;
s = (-s1.y * (p0.x - p2.x) + s1.x * (p0.y - p2.y)) / (-s2.x * s1.y + s1.x * s2.y);
t = ( s2.x * (p0.y - p2.y) - s2.y * (p0.x - p2.x)) / (-s2.x * s1.y + s1.x * s2.y);
if(s >= 0.0f && s <= 1.0f && t >= 0.0f && t <= 1.0f) {
result.x = p0.x + (t * s1.x);
result.y = p0.y + (t * s1.y);
return true;
}
return false;
}
void MistShape::reset() {
scale_speed = rx_random(1.05f, 1.1f);
rotate_speed = rx_random(0.1, 0.15f);
lifetime = rx_random(3.5f, 7.5f);
die_time = rx_millis() + lifetime;
}
Mist::Mist(Effects& effects)
:BaseEffect(effects, EFFECT_MIST)
,bytes_allocated(0)
,vert(0)
,frag(0)
,prog(0)
,vbo(0)
,vao(0)
,mist_tex(0)
,u_time(0)
,u_perc(0)
,needs_update(false)
{
}
Mist::~Mist() {
}
bool Mist::setup() {
vert = rx_create_shader(GL_VERTEX_SHADER, MIST_VS);
frag = rx_create_shader(GL_FRAGMENT_SHADER, MIST_FS);
prog = rx_create_program(vert, frag);
glBindAttribLocation(prog, 0, "a_pos");
glBindAttribLocation(prog, 1, "a_tex");
glLinkProgram(prog);
rx_print_shader_link_info(prog);
glUseProgram(prog);
glUniform1i(glGetUniformLocation(prog, "u_tex"), 0);
u_time = glGetUniformLocation(prog, "u_time");
u_perc = glGetUniformLocation(prog, "u_perc");
assert(u_time >= 0);
assert(u_perc >= 0);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexPT3), (GLvoid*)0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexPT3), (GLvoid*)12);
createRing(1024 * 0.5, 768 * 0.5, 110.0f, 150.0);
MistShape shape;
shape.reset();
shape.offset = offsets.back();
shape.count = counts.back();
shape.x = 1024 * 0.5;
shape.y = 768 * 0.5;
shapes.push_back(shape);
mist_tex = rx_create_texture(rx_to_data_path("images/mist.png")); // , GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
if(!mist_tex) {
printf("Error: cannot create the mist texture.\n");
return false;
}
return true;
}
void Mist::update() {
if(!needs_update) {
return;
}
size_t needed = sizeof(VertexPT3) * vertices.size();
if(!needed) {
return;
}
glBindBuffer(GL_ARRAY_BUFFER, vbo);
if(needed > bytes_allocated) {
glBufferData(GL_ARRAY_BUFFER, needed, vertices[0].ptr(), GL_DYNAMIC_DRAW);
bytes_allocated = needed;
}
else {
glBufferSubData(GL_ARRAY_BUFFER, 0, needed, vertices[0].ptr());
}
needs_update = false;
}
void Mist::draw() {
#if USE_MIST_DEBUG
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#endif
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mist_tex);
glUseProgram(prog);
glBindVertexArray(vao);
glUniformMatrix4fv(glGetUniformLocation(prog, "u_pm"), 1, GL_FALSE, effects.ortho_pm.ptr());
for(std::vector<MistShape>::iterator it = shapes.begin(); it != shapes.end(); ++it) {
MistShape& shape = *it;
glUniform1f(u_time, rx_millis() * shape.rotate_speed);
glUniform1f(u_perc, perc);
shape.mm.identity();
shape.mm.translate(shape.x, shape.y, 0.0f);
glUniformMatrix4fv(glGetUniformLocation(prog, "u_mm"), 1, GL_FALSE, shape.mm.ptr());
glDrawArrays(GL_TRIANGLES, shape.offset, shape.count);
}
}
void Mist::createRing(float x, float y, float radius, float width) {
offsets.push_back(vertices.size());
float resolution = 16.0;
float angle = TWO_PI/resolution;
float outer_radius = radius + width;
for(int i = 0; i < resolution; ++i) {
float c0 = cos( (i + 0) * angle);
float s0 = sin( (i + 0) * angle);
float c1 = cos( (i + 1) * angle);
float s1 = sin( (i + 1) * angle);
// positions
vec3 pa(c0 * radius, s0 * radius, 0.0f);
vec3 pb(c1 * radius, s1 * radius, 0.0f);
vec3 pc(c1 * outer_radius, s1 * outer_radius, 0.0f);
vec3 pd(c0 * outer_radius, s0 * outer_radius, 0.0f);
// texcoords
float u0 = float(i+0)/resolution;
float u1 = float(i+1)/resolution;
vec3 ta(u0, 0.0f, 1.0f);
vec3 tb(u1, 0.0f, 1.0f);
vec3 tc(u1, 1.0f, 1.0f);
vec3 td(u0, 1.0f, 1.0f);
// calculate distances from the corners to the centers
vec3 intersection;
if(!intersect(pa, pc, pb, pd, intersection)) {
printf("The vertices of the dist do not intersect. Error.\n");
::exit(EXIT_FAILURE);
}
float d0 = length(pa - intersection);
float d1 = length(pb - intersection);
float d2 = length(pc - intersection);
float d3 = length(pd - intersection);
ta = ta * ((d0 + d2)/d2);
tb = tb * ((d1 + d3)/d3);
tc = tc * ((d2 + d0)/d0);
td = td * ((d3 + d1)/d1);
// store the vertices
VertexPT3 a(pa,ta);
VertexPT3 b(pb,tb);
VertexPT3 c(pc,tc);
VertexPT3 d(pd,td);
vertices.push_back(a);
vertices.push_back(b);
vertices.push_back(c);
vertices.push_back(a);
vertices.push_back(c);
vertices.push_back(d);
}
counts.push_back(vertices.size()-offsets.back());
needs_update = true;
}
#ifndef SWNT_MIST_EFFECT_H
#define SWNT_MIST_EFFECT_H
#define USE_MIST_DEBUG 0
#include <swnt/Types.h>
#include <swnt/effects/BaseEffect.h>
#include <vector>
static const char* MIST_VS = ""
"#version 150\n"
"uniform mat4 u_pm;"
"uniform mat4 u_mm;"
"in vec4 a_pos;"
"in vec3 a_tex;"
"out vec3 v_tex;"
"void main() {"
" gl_Position = u_pm * u_mm * a_pos; "
// " gl_Position = vec4(2.0 * a_tex - vec2(1.0), 0.0, 1.0);" // check if texture coords are mapped correctly
" v_tex = a_tex;"
"}"
"";
static const char* MIST_FS = ""
"#version 150\n"
"uniform sampler2D u_tex;"
"uniform float u_time;"
"uniform float u_perc;"
"out vec4 fragcolor;"
"in vec3 v_tex;"
"void main() {"
" vec4 diffuse_color = texture(u_tex, v_tex.xy / v_tex.z);"
" fragcolor.rgb = diffuse_color.rgb;"
" fragcolor.a = sin(3.1415 * 0.5 * u_perc);"
"}"
"";
struct MistShape {
void reset();
size_t offset;
size_t count;
mat4 mm;
float scale_speed;
float rotate_speed;
float die_time;
float lifetime;
float x;
float y;
};
class Mist : public BaseEffect {
public:
Mist(Effects& effects);
~Mist();
bool setup();
void update();
void draw();
private:
void createRing(float x, float y, float radius, float width);
private:
GLuint vert;
GLuint frag;
GLuint prog;
GLuint vbo;
GLuint vao;
GLuint mist_tex;
GLint u_time;
GLint u_perc;
std::vector<VertexPT3> vertices;
std::vector<GLsizei> counts;
std::vector<GLint> offsets;
std::vector<MistShape> shapes;
size_t bytes_allocated;
bool needs_update;
};
#endif
@roxlu
Copy link
Author

roxlu commented Jan 7, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment