Skip to content

Instantly share code, notes, and snippets.

@cmbruns
Last active September 13, 2020 18:06
Show Gist options
  • Save cmbruns/5b1c2b211766cdcb3b29689e0c32a63d to your computer and use it in GitHub Desktop.
Save cmbruns/5b1c2b211766cdcb3b29689e0c32a63d to your computer and use it in GitHub Desktop.
#version 450 core
// Vertex shader for rendering an infinite plane.
void main() {} // vertex shader does nothing. All the work is done in the geometry shader
#version 450 core
// Geometry shader for rendering an infinite plane.
// Creates a screen-space imposter polygon for rendering the plane.
// Most of the polygon edges lie along the screen edges.
// Call from host side with glDrawArrays(GL_POINTS, 0, 1)
layout (points) in;
layout (triangle_strip) out;
layout (max_vertices = 5) out;
layout(location = 1) uniform mat4 projection = mat4(1);
layout(location = 2) uniform mat4 view = mat4(1);
layout(location = 3) uniform mat4 model = mat4(1);
// corners of the display screen in counter-clockwise order
// in clip space (normalized device coordinates)
const float sz = 0.5; // Exact z value does not matter
const int num_corners = 4;
const vec4 screen_quad[num_corners] = vec4[num_corners](
vec4(-1, -1, sz, 1), // lower left
vec4( 1, -1, sz, 1), // lower right
vec4( 1, 1, sz, 1), // upper right
vec4(-1, 1, sz, 1)); // upper left
// Output coordinates of view/plane intersection in view space
out vec4 point;
vec3 dehomog(vec4 v)
{
return v.xyz/v.w;
}
struct EdgeVertex
{
vec4 pos; // screen edge location in clip space
vec4 point; // view/plane intersection in view space
};
void emit_vertex(in EdgeVertex v)
{
gl_Position = v.pos;
point = v.point;
EmitVertex();
}
void main()
{
int vertex_count = 0;
EdgeVertex e = EdgeVertex(vec4(0), vec4(0));
EdgeVertex vertices[5] = EdgeVertex[5](e, e, e, e, e);
// Perform all computations in view space
mat4 MV = view * model;
vec3 N = normalize((MV * vec4(0, 1, 0, 0))).xyz; // plane normal
vec3 T = dehomog(MV * vec4(0, 0, 0, 1)); // plane translation
float d = -dot(N, T);
mat4 Pinv = inverse(projection);
// Walk around the display screen in counter-clockwise direction,
// and add vertices as needed.
for (int c = 0; c < num_corners; ++c)
{
vec3 D = (Pinv * screen_quad[c]).xyz; // view direction
float w = dot(D, N); // plane determinant
// does this corner overlap the view of the plane?
if (w * d < 0) {
// plane overlaps this corner, so use this vertex
vertices[vertex_count].point = vec4(-d * D, w);
vertices[vertex_count].pos = screen_quad[c];
++vertex_count;
}
// does the plane edge cross the screen edge between this corner and the next?
int c2 = (c + 1) % num_corners;
vec3 D2 = (Pinv * screen_quad[c2]).xyz;
float w2 = dot(D2, N);
if (w * w2 < 0) {
// plane edge crosses the screen edge before the next corner
// interpolate and add a vertex
float alpha = w / (w - w2);
vec3 Dmid = mix(D, D2, alpha);
vec4 posMid = mix(screen_quad[c], screen_quad[c2], alpha);
vertices[vertex_count].point = vec4(-d * Dmid, dot(Dmid, N));
vertices[vertex_count].pos = posMid;
++vertex_count;
}
}
if (vertex_count < 3)
return; // not enough for even one triangle
// create triangle strip order by staggering the vertices
int order[] = int[](0, 1, vertex_count - 1, 2, vertex_count - 2, 3);
for (int v = 0; v < vertex_count; ++v)
emit_vertex(vertices[order[v]]);
EndPrimitive();
}
#version 450 core
uniform sampler2D image;
layout(location = 1) uniform mat4 projection = mat4(1);
layout(location = 2) uniform mat4 view = mat4(1);
layout(location = 3) uniform mat4 model = mat4(1);
in vec4 point;
out vec4 frag_color;
float texture_coordinate(vec3 direction, vec3 translation)
{
vec3 uhat = (view * model * vec4(direction, 0)).xyz;
float uscale = length(uhat);
uhat /= uscale * uscale;
float du = -dot(translation, uhat);
// above uhat/du could be computed on the host
// below is per-fragment
float u = dot(uhat, point.xyz)/point.w + du;
return u;
}
vec3 dehomog(vec4 v) { return v.xyz/v.w; }
vec2 texture_coordinates()
{
vec3 T = dehomog(view * model * vec4(0, 0, 0, 1));
float u = texture_coordinate(vec3(1, 0, 0), T);
float v = texture_coordinate(vec3(0, 0, 1), T);
return vec2(u, v);
}
void color_by_texture() {
vec2 tc = texture_coordinates();
frag_color = texture(image, tc);
}
void color_solid() {
frag_color = vec4(0.1, 0.1, 0.8, 1);
}
void set_depth() {
vec4 ndc = projection * point;
gl_FragDepth = (ndc.z / ndc.w + 1.0) / 2.0;
}
void main()
{
color_by_texture();
set_depth();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment