diff --git a/Effects/landmass.eff b/Effects/landmass.eff index d7141c06e..1f6114987 100644 --- a/Effects/landmass.eff +++ b/Effects/landmass.eff @@ -87,7 +87,7 @@ Shaders/landmass-g.vert Shaders/landmass.geom Shaders/landmass.frag - 20 + 18 triangles triangle-strip diff --git a/Shaders/landmass-g.vert b/Shaders/landmass-g.vert index fe89ab7a3..afcffa7db 100644 --- a/Shaders/landmass-g.vert +++ b/Shaders/landmass-g.vert @@ -1,19 +1,55 @@ +#version 120 + +// "landmass" effect with forest construction using a geometry +// shader. The landmass effect includes a bumpmap effect as well as +// variation by altitude. +// +// The fragment shader needs position and normals in model and eye +// coordinates. This vertex shader calculates the positions and +// normals of the forest polygons so the geometry shader can do as +// little as possible. + +// Input for the geometry shader. "raw" means terrain model +// coordinates; that's a tile-local coordinate system, with z as local +// up. "ec" means eye coordinates. + +// model position of original terrain poly; the bottom of the forest. varying vec4 rawposIn; +// model forest top +varying vec4 rawTopIn; +// model normal varying vec3 NormalIn; +varying vec4 ecPosIn; +varying vec4 ecTopIn; +varying vec3 ecNormalIn; +// eye spacce tangent and binormal varying vec3 VTangentIn; varying vec3 VBinormalIn; +// screen-space position of top +varying vec4 positionTopIn; +// constant color component +varying vec4 constantColorIn; attribute vec3 tangent; attribute vec3 binormal; +uniform float canopy_height; + void main(void) { - rawposIn = gl_Vertex; + rawposIn = gl_Vertex; + ecPosIn = gl_ModelViewMatrix * gl_Vertex; NormalIn = normalize(gl_Normal); + rawTopIn = rawposIn + vec4(0.0, 0.0, canopy_height, 0.0); + ecTopIn = gl_ModelViewMatrix * rawTopIn; + ecNormalIn = gl_NormalMatrix * NormalIn; VTangentIn = gl_NormalMatrix * tangent; VBinormalIn = gl_NormalMatrix * binormal; gl_FrontColor = gl_Color; gl_Position = ftransform(); + positionTopIn = gl_ModelViewProjectionMatrix * rawTopIn; gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + constantColorIn = gl_FrontMaterial.emission + + gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient); } diff --git a/Shaders/landmass.geom b/Shaders/landmass.geom index 01bc9f095..5dc4a8bd4 100644 --- a/Shaders/landmass.geom +++ b/Shaders/landmass.geom @@ -1,82 +1,116 @@ #version 120 #extension GL_EXT_geometry_shader4 : enable +// Geometry shader that creates a prism from a terrain triangle, +// resulting in a forest effect. +// +// A geometry shader should do as little computation as possible. + +// See landmass-g.vert for a description of the inputs. varying in vec4 rawposIn[]; varying in vec3 NormalIn[]; +varying in vec4 rawTopIn[]; +varying in vec4 ecPosIn[]; +varying in vec4 ecTopIn[]; +varying in vec3 ecNormalIn[]; varying in vec3 VTangentIn[]; varying in vec3 VBinormalIn[]; +varying in vec4 positionTopIn[]; +varying in vec4 constantColorIn[]; +uniform float canopy_height; + +// model position varying out vec4 rawpos; +// eye position varying out vec4 ecPosition; +// eye space surface matrix varying out vec3 VNormal; varying out vec3 VTangent; varying out vec3 VBinormal; +// model normal varying out vec3 Normal; varying out vec4 constantColor; varying out float bump; -uniform float canopy_height; - -void createVertex(int i, int j, float offset, float s) +// Emit one vertex of the forest geometry. +// parameters: +// i - index into original terrain triangle +void doVertex(in int i, in vec4 pos, in vec4 ecpos, in vec4 screenpos, + in vec3 rawNormal, in vec3 normal, in float s) { - rawpos = rawposIn[i] + offset * vec4(NormalIn[i], 0.0); - ecPosition = gl_ModelViewMatrix * rawpos; - if ( s == 0.0 ) - { - vec4 v; - if (j < i) - { - v = rawposIn[j] - rawposIn[i]; - Normal = normalize(cross( NormalIn[i], v.xyz )); - } - else - { - v = rawposIn[i] - rawposIn[j]; - Normal = normalize(cross( NormalIn[j], v.xyz )); - } - } - else - { - Normal = NormalIn[i]; - } - VNormal = normalize(gl_NormalMatrix * Normal); - VTangent = VTangentIn[i]; + rawpos = pos; + ecPosition = ecpos; + Normal = rawNormal; + VNormal = normal; + VTangent = VTangentIn[i]; VBinormal = VBinormalIn[i]; bump = s; gl_FrontColor = gl_FrontColorIn[i]; - constantColor = gl_FrontMaterial.emission - + gl_FrontColorIn[i] * (gl_LightModel.ambient + gl_LightSource[0].ambient); - gl_Position = gl_ProjectionMatrix * ecPosition; + constantColor = constantColorIn[i]; + gl_Position = screenpos; gl_TexCoord[0] = gl_TexCoordIn[i][0]; EmitVertex(); } +vec3 rawSideNormal[3]; +vec3 sideNormal[3]; + +// Emit a vertex for a forest side triangle +void doSideVertex(in int vertIdx, in int sideIdx, vec4 pos, in vec4 ecpos, + in vec4 screenpos) +{ + doVertex(vertIdx, pos, ecpos, screenpos, rawSideNormal[sideIdx], + sideNormal[sideIdx], 0.0); +} + void main(void) { - if (canopy_height > 0.01) { - createVertex(0, 1, canopy_height, 0.0); - createVertex(0, 1, 0.0, 0.0); - createVertex(1, 0, canopy_height, 0.0); - createVertex(1, 0, 0.0, 0.0); - EndPrimitive(); - createVertex(1, 2, canopy_height, 0.0); - createVertex(1, 2, 0.0, 0.0); - createVertex(2, 1, canopy_height, 0.0); - createVertex(2, 1, 0.0, 0.0); - EndPrimitive(); - createVertex(2, 0, canopy_height, 0.0); - createVertex(2, 0, 0.0, 0.0); - createVertex(0, 2, canopy_height, 0.0); - createVertex(0, 2, 0.0, 0.0); - EndPrimitive(); - createVertex(0, 1, 0.0, 0.0); - createVertex(1, 2, 0.0, 0.0); - createVertex(2, 0, 0.0, 0.0); - EndPrimitive(); - } - createVertex(0, 1, canopy_height, 1.0); - createVertex(1, 2, canopy_height, 1.0); - createVertex(2, 0, canopy_height, 1.0); - EndPrimitive(); + rawSideNormal[0] = normalize(cross((rawposIn[1] - rawposIn[0]).xyz, + NormalIn[0])); + rawSideNormal[1] = normalize(cross((rawposIn[2] - rawposIn[1]).xyz, + NormalIn[1])); + rawSideNormal[2] = normalize(cross((rawposIn[0] - rawposIn[2]).xyz, + NormalIn[2])); + for (int i = 0; i < 3; ++i) + sideNormal[i] = gl_NormalMatrix * rawSideNormal[i]; + if (canopy_height > 0.01) { + // Sides + doSideVertex(0, 0, rawTopIn[0], ecTopIn[0], positionTopIn[0]); + doSideVertex(0, 0, rawposIn[0], ecPosIn[0], gl_PositionIn[0]); + doSideVertex(1, 0, rawTopIn[1], ecTopIn[1], positionTopIn[1]); + doSideVertex(1, 0, rawposIn[1], ecPosIn[1], gl_PositionIn[1]); + + doSideVertex(2, 1, rawTopIn[2], ecTopIn[2], positionTopIn[2]); + doSideVertex(2, 1, rawposIn[2], ecPosIn[2], gl_PositionIn[2]); + + doSideVertex(0, 2, rawTopIn[0], ecTopIn[0], positionTopIn[0]); + doSideVertex(0, 2, rawposIn[0], ecPosIn[0], gl_PositionIn[0]); + // Degenerate triangles; avoids EndPrimitive() + doSideVertex(0, 2, rawposIn[0], ecPosIn[0], gl_PositionIn[0]); + doVertex(0, rawTopIn[0], ecTopIn[0], positionTopIn[0], NormalIn[0], + ecNormalIn[0], 1.0); + // Top + } + doVertex(0, rawTopIn[0], ecTopIn[0], positionTopIn[0], NormalIn[0], + ecNormalIn[0], 1.0); + doVertex(1, rawTopIn[1], ecTopIn[1], positionTopIn[1], NormalIn[1], + ecNormalIn[1], 1.0); + doVertex(2, rawTopIn[2], ecTopIn[2], positionTopIn[2], NormalIn[2], + ecNormalIn[2], 1.0); + // Don't render "bottom" triangle for now; it's hidden. +#if 0 + // degenerate + doVertex(2, rawTopIn[2], ecTopIn[2], positionTopIn[2], NormalIn[2], + ecNormalIn[2], 1.0); + // bottom + doVertex(0, rawposIn[0], ecPosIn[0], gl_PositionIn[0], NormalIn[0], + ecNormalIn[0], 1.0); + doVertex(1, rawposIn[1], ecPosIn[1], gl_PositionIn[1], NormalIn[1], + ecNormalIn[1], 1.0); + doVertex(2, rawposIn[2], ecPosIn[2], gl_PositionIn[2], NormalIn[2], + ecNormalIn[2], 1.0); +#endif + EndPrimitive(); }