diff options
author | Eric Anholt <eric@anholt.net> | 2013-11-20 16:52:28 -0800 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2013-11-20 16:52:28 -0800 |
commit | 2c900c0b695ab54c1646325db9a0c8296bcffb46 (patch) | |
tree | e809c631320b4e2977f7974d87aebd284a9a2038 | |
parent | 6ac9d7fd38cfd49b087a3ed75778bad1cbec58e8 (diff) |
Add the orbital-explorer shaders that Paul sent me.
-rw-r--r-- | shaders/orbital_explorer.shader_test | 636 |
1 files changed, 636 insertions, 0 deletions
diff --git a/shaders/orbital_explorer.shader_test b/shaders/orbital_explorer.shader_test new file mode 100644 index 0000000..70ea084 --- /dev/null +++ b/shaders/orbital_explorer.shader_test @@ -0,0 +1,636 @@ +# Shader_runner test illustrating slow compilation of geometry shaders +# in mesa/i965. +# +# I've made the following changes from the original shaders: +# - Change shaderDepth from a texture to a uniform float +# +# The shaders in this test come from +# https://github.com/bjthinks/orbital-explorer, which contains this +# copyright notice: +# +# This file is part of the Electron Orbital Explorer. The Electron +# Orbital Explorer is distributed under the Simplified BSD License +# (also called the "BSD 2-Clause License"), in hopes that these +# rendering techniques might be used by other programmers in +# applications such as scientific visualization, video gaming, and so +# on. If you find value in this software and use its technologies for +# another purpose, I would love to hear back from you at bjthinks (at) +# gmail (dot) com. If you improve this software and agree to release +# your modifications under the below license, I encourage you to fork +# the development tree on github and push your modifications. The +# Electron Orbital Explorer's development URL is: +# https://github.com/bjthinks/orbital-explorer +# (This paragraph is not part of the software license and may be +# removed.) +# +# Copyright (c) 2013, Brian W. Johnson +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# + Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# + Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +[require] +GLSL >= 1.50 + +[vertex shader] +#version 150 + +uniform mat4 modelViewProjMatrix; + +in vec4 position; +in vec3 rim; +out vec4 inverted_position; +out vec3 integrand; + +// Note sure what this transform is called, but since doing it twice +// gives back the original vector, we'll call it a "vector inverse" +// for now. +// (x, y, z, w) --> (x/w, y/w, z/w, 1/w) +vec4 vector_inverse(vec4 a) +{ + float w = a.w; + a.w = 1.0; + a /= w; + return a; +} + +// Calculate the position and inverted position of the vertex, setting +// the output varyings "gl_Position" and "inverted_position". +void calculate_position() +{ + vec4 pos = modelViewProjMatrix * position; + gl_Position = pos; + inverted_position = vector_inverse(pos); +} + +// Apply brightness adjustment and color rotation to the input color, +// setting the output varying "integrand" to transformed color +// coordinates that are sensible to integrate. +void calculate_color() +{ + integrand = rim; +} + +void main(void) +{ + calculate_position(); + calculate_color(); +} + + +[geometry shader] +#version 150 + +layout(lines_adjacency) in; +layout(triangle_strip,max_vertices=21) out; + +uniform vec2 nearfar; + +in vec4 inverted_position[4]; +in vec3 integrand[4]; + +noperspective out float one_over_w_front; +noperspective out float one_over_w_back; +noperspective out vec3 integrand_over_w_front; +noperspective out vec3 integrand_over_w_back; +noperspective out vec2 texPosition; + +void swap4(inout vec4 a, inout vec4 b) +{ + vec4 temp = a; + a = b; + b = temp; +} + +void swap3(inout vec3 a, inout vec3 b) +{ + vec3 temp = a; + a = b; + b = temp; +} + +void outputVertex(vec4 nfront, vec4 nback, vec3 ifront, vec3 iback) +{ + one_over_w_front = nfront.w; + one_over_w_back = nback.w; + integrand_over_w_front = ifront * nfront.w; + integrand_over_w_back = iback * nback.w; + gl_Position = vec4(nfront.xy, 0.0, 1.0); + texPosition = (nfront.xy + 1.0) / 2.0; + EmitVertex(); +} + +void outputSimpleVertex(vec4 n, vec3 i) +{ + one_over_w_front = one_over_w_back = n.w; + integrand_over_w_front = integrand_over_w_back = i * n.w; + gl_Position = vec4(n.xy, 0.0, 1.0); + texPosition = (n.xy + 1.0) / 2.0; + EmitVertex(); +} + +void good_case(vec4 n0, vec4 n1, vec4 n2, vec4 n3, + vec3 i0, vec3 i1, vec3 i2, vec3 i3) +{ + // We need the point on the big face that intersects the middle point + // in screen cooordinates. To do this, we need to solve the following + // system of equations in the xy plane: + // n0 = n1 + t (n2 - n1) + u (n3 - n1) + // Rearranging, we get: + // (n2 - n1) t + (n3 - n1) u = n0 - n1 + // Which we write abstractly as: + // A * X = B, where + mat2 A = mat2((n2 - n1).xy, (n3 - n1).xy); + vec2 B = (n0 - n1).xy; + vec2 X = inverse(A) * B; + float t = X[0]; + float u = X[1]; + vec4 weighted_x1 = n1 * (1 - t - u); + vec4 weighted_x2 = n2 * t; + vec4 weighted_x3 = n3 * u; + vec4 ny = weighted_x1 + weighted_x2 + weighted_x3; + vec3 iy = weighted_x1.w * i1 + weighted_x2.w * i2 + weighted_x3.w * i3; + iy /= ny.w; + if (n0.z > ny.z) { + swap4(n0, ny); + swap3(i0, iy); + } + + // Draw three triangles. + // x[1] - x[2] - y + outputSimpleVertex(n1, i1); + outputSimpleVertex(n2, i2); + outputVertex(n0, ny, i0, iy); + + // x[2] - y - x[3] + outputSimpleVertex(n3, i3); + + // y - x[3] - x[1] + outputSimpleVertex(n1, i1); + EndPrimitive(); +} + +void bad_case(vec4 n0, vec4 n1, vec4 n2, vec4 n3, + vec3 i0, vec3 i1, vec3 i2, vec3 i3) +{ + // Compute the intersection point of segment 01 with segment 23 + // We need to solve this system of equations in the xy plane: + // n0 + t (n1 - n0) = n2 + u (n3 - n2) + // which, by algebra, is: + // (n1 - n0) t + (n2 - n3) u = n2 - n0 + // Which we will write abstractly as: + // A * X = B + // Note that doing this calculation with x[].n instead of x[].v + // will give us correct x, y, z values regardless of w. + mat2 A = mat2((n1 - n0).xy, (n2 - n3).xy); + vec2 B = (n2 - n0).xy; + vec2 X = inverse(A) * B; // NOTE: need to handle ill-conditioned case + float t = X[0]; + float u = X[1]; + + // We get two answers; one is the point on the front edge which + // appears to intersect the back edge in screen coordinates, and + // the other is the point on the back edge which appears to inter- + // sect the front edge in screen coordinates. We don't know which + // is which, but we can tell the difference by looking at the z + // values. + // The two intersection points are: + vec4 weighted_x0 = n0 * (1 - t); + vec4 weighted_x1 = n1 * t; + vec4 ny0 = weighted_x0 + weighted_x1; + vec3 iy0 = weighted_x0.w * i0 + weighted_x1.w * i1; + iy0 /= ny0.w; + + vec4 weighted_x2 = n2 * (1 - u); + vec4 weighted_x3 = n3 * u; + vec4 ny1 = weighted_x2 + weighted_x3; + vec3 iy1 = weighted_x2.w * i2 + weighted_x3.w * i3; + iy1 /= ny1.w; + + // Make sure y0 is front and y1 is back + if (ny0.z > ny1.z) { + swap4(ny0, ny1); + swap3(iy0, iy1); + } + + // Draw four triangles. + // x[0] - x[2] - y + outputSimpleVertex(n0, i0); + outputSimpleVertex(n2, i2); + outputVertex(ny0, ny1, iy0, iy1); + + // x[2] - y - x[1] + outputSimpleVertex(n1, i1); + + // A null triangle: y - x[1] - y + outputVertex(ny0, ny1, iy0, iy1); + + // x[1] - y - x[3] + outputSimpleVertex(n3, i3); + + // y - x[3] - x[0] + outputSimpleVertex(n0, i0); + EndPrimitive(); +} + +// Return +/- 1, depending on the orientation of triangle a -> b -> c +// in the xy plane (ignoring z and w) +float triangle_orientation(vec4 a, vec4 b, vec4 c) +{ + vec2 ab = b.xy - a.xy; + vec2 ac = c.xy - a.xy; + return sign(ab.x * ac.y - ab.y * ac.x); +} + +void handle_tetrahedron(vec4 n0, vec4 n1, vec4 n2, vec4 n3, + vec3 i0, vec3 i1, vec3 i2, vec3 i3) +{ + // For each face of the tetrahedron, an orientation on that face + // is induced by the orientation of the input tetrahedron. Compute + // whether those orientations are equal to or opposite from the + // orientation in screen coordinates. + // The orientation will be the same for front faces and reversed + // for back faces (or vice versa, depending on whether the + // tetrahedron is left or right handed). + // Surprisingly, we DON'T need to know the handedness of the input + // tetrahedron for our rendering calculations! + // The induced orientations are: + // Face 0: 3 -> 2 -> 1 + float orient0 = triangle_orientation(n3, n2, n1); + // Face 1: 0 -> 2 -> 3 + float orient1 = triangle_orientation(n0, n2, n3); + // Face 2: 3 -> 1 -> 0 + float orient2 = triangle_orientation(n3, n1, n0); + // Face 3: 0 -> 1 -> 2 + float orient3 = triangle_orientation(n0, n1, n2); + + float orient01 = orient0 * orient1; + float orient23 = orient2 * orient3; + float orient = orient01 * orient23; + + // For now, discard tetrahedra where a face is seen exactly edge-on. + // The sign() function should return 0.0 in that case, but exercise + // an abundance of caution with floating point math. + if (orient > -0.5 && orient < 0.5) + return; + + // We call a tetrahedron good if its projection onto the canvas + // has a triangular convex hull with one vertex in the interior. + // It is conversely bad if the convex hull is a quadrilateral. + // A tetrahedron is good iff an odd number of faces have flipped + // orientation, i.e. there are either three front and one rear + // faces or vice versa. + bool good = orient < 0.; + + if (good) { + // The vertex in the center is the one whose orientN value is + // of a different sign. Figure out the majority sign and which + // vertex is in the middle. + if (orient01 > 0.) { + // 0 and 1 have the same sign; either 2 or 3 is opposite + float majority = orient0; + if (orient2 * majority < 0.) { + swap4(n0, n2); + swap3(i0, i2); + } else { + swap4(n0, n3); + swap3(i0, i3); + } + } else { + // 2 and 3 have the same sign; either 0 or 1 is opposite + float majority = orient2; + if (orient0 * majority < 0.) + ; + else { + swap4(n0, n1); + swap3(i0, i1); + } + } + + good_case(n0, n1, n2, n3, + i0, i1, i2, i3); + + } else { + float orient02 = orient0 * orient2; + + // We assume here that two of the orientations are positive, and + // two are negative. The other possibility, that all four have + // the same sign, would indicate an error; it's unclear if that + // can happen at all, but it's probably worth checking for. + if (orient01 > 0. && orient23 > 0. && orient02 > 0.) + return; + + // Reorder so that vertices 0 and 1 have the same sign + if (orient01 > 0.) + // 0 and 1 already match, do nothng + ; + else if (orient02 > 0.) { + // 0 and 2 match, so swap 1 with 2 + swap4(n1, n2); + swap3(i1, i2); + } else { + // 0 and 3 match, so swap 1 with 3 + swap4(n1, n3); + swap3(i1, i3); + } + + bad_case(n0, n1, n2, n3, i0, i1, i2, i3); + } +} + +vec4 vector_inverse(vec4 a) +{ + float w = a.w; + a.w = 1.0; + a /= w; + return a; +} + +void intersect_nearclip(out vec4 nc, out vec3 ic, + vec4 px, vec3 ix, vec4 py, vec3 iy) +{ + float near = nearfar[0]; + float t = (near - py.w) / (px.w - py.w); + nc = vector_inverse(t * px + (1-t) * py); + ic = t * ix + (1-t) * iy; +} + +void main(void) +{ + vec4 p0 = gl_in[0].gl_Position; + vec4 p1 = gl_in[1].gl_Position; + vec4 p2 = gl_in[2].gl_Position; + vec4 p3 = gl_in[3].gl_Position; + + // Number of vertices clipped by the near plane + int num_clipped = 0; + + // Count how many vertices are clipped + float near = nearfar[0]; + if (p0.w < near) + ++num_clipped; + if (p1.w < near) + ++num_clipped; + if (p2.w < near) + ++num_clipped; + if (p3.w < near) + ++num_clipped; + + if (num_clipped == 4) + return; + + vec4 n0 = inverted_position[0]; + vec4 n1 = inverted_position[1]; + vec4 n2 = inverted_position[2]; + vec4 n3 = inverted_position[3]; + + vec3 i0 = integrand[0]; + vec3 i1 = integrand[1]; + vec3 i2 = integrand[2]; + vec3 i3 = integrand[3]; + + if (num_clipped == 0) { + handle_tetrahedron(n0, n1, n2, n3, i0, i1, i2, i3); + return; + } + + // Handle the near clipping plane. This is the "slow" execution path. + + // Move clipped vertices to the front of the array using a "sorting network" + if (p0.w >= near && p2.w < near) { + swap4(p0, p2); + swap4(n0, n2); + swap3(i0, i2); + } + if (p1.w >= near && p3.w < near) { + swap4(p1, p3); + swap4(n1, n3); + swap3(i1, i3); + } + if (p0.w >= near && p1.w < near) { + swap4(p0, p1); + swap4(n0, n1); + swap3(i0, i1); + } + if (p2.w >= near && p3.w < near) { + swap4(p2, p3); + swap4(n2, n3); + swap3(i2, i3); + } + if (p1.w >= near && p2.w < near) { + swap4(p1, p2); + swap4(n1, n2); + swap3(i1, i2); + } + + switch (num_clipped) { + + case 1: + + { + // One vertex clipped: + // Must determine the intersections of the 0-to-i segments + // with the near clipping plane, and construct three new tetrahedra + // to render. + + vec4 n03; + vec3 i03; + intersect_nearclip(n03, i03, p0, i0, p3, i3); + handle_tetrahedron(n1, n2, n3, n03, + i1, i2, i3, i03); + + vec4 n02; + vec3 i02; + intersect_nearclip(n02, i02, p0, i0, p2, i2); + handle_tetrahedron(n1, n2, n02, n03, + i1, i2, i02, i03); + + vec4 n01; + vec3 i01; + intersect_nearclip(n01, i01, p0, i0, p1, i1); + handle_tetrahedron(n1, n01, n02, n03, + i1, i01, i02, i03); + } + + return; + + case 2: + + { + // Two vertices clipped: + // Must determine the intersections of the (0,1)-to-(2,3) segments + // with the near clipping plane, and construct three new tetrahedra + // to render. + vec4 n02; + vec3 i02; + intersect_nearclip(n02, i02, p0, i0, p2, i2); + + vec4 n12; + vec3 i12; + intersect_nearclip(n12, i12, p1, i1, p2, i2); + + handle_tetrahedron(n2, n3, n02, n12, + i2, i3, i02, i12); + + vec4 n03; + vec3 i03; + intersect_nearclip(n03, i03, p0, i0, p3, i3); + + handle_tetrahedron(n3, n02, n12, n03, + i3, i02, i12, i03); + + vec4 n13; + vec3 i13; + intersect_nearclip(n13, i13, p1, i1, p3, i3); + + handle_tetrahedron(n3, n12, n03, n13, + i3, i12, i03, i13); + } + + return; + + case 3: + + // Three vertices clipped: + // Must replace 0-2 with their projections toward 3 at + // the point they intersect the near clipping plane. + vec4 n03; + vec3 i03; + intersect_nearclip(n03, i03, p0, i0, p3, i3); + + vec4 n13; + vec3 i13; + intersect_nearclip(n13, i13, p1, i1, p3, i3); + + vec4 n23; + vec3 i23; + intersect_nearclip(n23, i23, p2, i2, p3, i3); + + handle_tetrahedron(n03, n13, n23, n3, + i03, i13, i23, i3); + + return; + + default: + + return; + + } +} + +[fragment shader] +#version 150 + +noperspective in float one_over_w_front; +noperspective in float one_over_w_back; +noperspective in vec3 integrand_over_w_front; +noperspective in vec3 integrand_over_w_back; +noperspective in vec2 texPosition; +out vec3 integratedValue; +uniform float solidDepth; // was: uniform sampler2D solidDepth; +uniform vec2 nearfar; +uniform bool depth_obscuration; + +// Extract the w value (which is the pre-projection z value) of the +// solid object from the depth buffer that was used in the solid +// object rendering pass. +float compute_solid_w() +{ + // The depth buffer from the solid object rendering pass stores + // something called z_w. + float z_w = solidDepth; // was: texture(solidDepth, texPosition).x; + + // We also need the "near" and "far" values from the frustum matrix, + // which are passed in as uniforms. + float n = nearfar[0]; + float f = nearfar[1]; + + // Also, z_d = z_c / w_c + // From the frustum matrix, + // z_c = -(f + n) / (f - n) * z - 2 * f * n / (f - n) * w + // w_c = -z + // Substituting, + // z_d = z_c / w_c = (f + n) / (f - n) + 2 * f * n / (f - n) * w / z + // But pre-frustum w = 1, so + // z_d = (f + n) / (f - n) + 2 * f * n / (f - n) / z + // z_d * (f - n) = f + n + 2 * f * n / z + // z_d * (f - n) - f - n = 2 * f * n / z + // z = 2 * f * n / (z_d * (f - n) - f - n) + // w = -z = -2 * f * n / (z_d * (f - n) - f - n) + // Substitute z_d = 2 * z_w - 1 + // w = -2 * f * n / ((2 * z_w - 1) * (f - n) - f - n) + // Simplify RHS + // w = -2 * f * n / (2 * z_w * (f - n) - 2 * f) + // w = f * n / (f - z_w * (f - n)) + return f * n / (f - z_w * (f - n)); +} + +void main(void) +{ + float w_front = 1.0 / one_over_w_front; + float w_back = 1.0 / one_over_w_back; + + vec3 integrand_front = w_front * integrand_over_w_front; + vec3 integrand_back = w_back * integrand_over_w_back; + + float solid_w = compute_solid_w(); + + if (solid_w <= w_front) + discard; + + if (solid_w < w_back) { + float t = (solid_w - w_front) / (w_back - w_front); + integrand_back = (1-t) * integrand_front + t * integrand_back; + w_back = solid_w; + } + + float depth = w_back - w_front; + vec3 integrand_middle = (integrand_front + integrand_back) / 2.0; + if (!depth_obscuration) { + integratedValue = depth * integrand_middle; + } else { + // Assume the z-component of the integrand represents magnitude, + // and calculate the falloff of all components based on its + // integral along the line of sight. + // TODO + } +} + +[vertex data] +position/float/4 rim/float/3 +-1 -1 -2 1 1 1 1 +-1 1 -3 1 1 1 1 + 1 -1 -3 1 1 1 1 + 1 1 -2 1 1 1 1 + +[test] +# Frustum with n=1 f=10 l=-1 r=1 b=-1 t=1 +uniform mat4 modelViewProjMatrix 1 0 0 0 0 1 0 0 0 0 -1.222 -1 0 0 -2.222 0 +uniform vec2 nearfar 1 10 +uniform float solidDepth 1.0 +uniform int depth_obscuration 0 +clear color 0 0 0 0 +clear +draw arrays GL_LINES_ADJACENCY 0 4 |