I am attempting to create a shader to produce a glowing halo effect in Three.js. My current attempt is live here: http://stemkoski.github.io/Three.js/Shader-Halo.html
The shader code is currently:
<script id="vertexShader" type="x-shader/x-vertex">
varying vec3 vNormal;
void main()
{
vNormal = normalize( normalMatrix * normal );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-vertex">
varying vec3 vNormal;
void main()
{
float intensity = pow( 0.7 - dot( vNormal, vec3( 0.0, 0.0, 1.0 ) ), 4.0 );
gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 ) * intensity;
}
</script>
This works fine provided the object remains at the center of the scene. After zooming or panning, the halo effect appears to change intensity or looks lopsided, respectively.
I think I understand why this is, mathematically -- in this code, the intensity of the glow effect is determined by the z-coordinate of the normal vector to the mesh, because that is the vector facing the viewer, and so I should somehow apply a similar transformation to (0,0,1) before calculating the intensity.
Thus I arrived at
<script id="vertexShader" type="x-shader/x-vertex">
varying vec3 vNormal;
varying vec4 vector;
void main()
{
vNormal = normalize( normalMatrix * normal );
vector = projectionMatrix * modelViewMatrix * vec4( 0.0, 0.0, 1.0, 0.0 );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-vertex">
varying vec3 vNormal;
varying vec4 vector;
void main()
{
vec4 v = vec4( vNormal, 0.0 );
float intensity = pow( 0.7 - dot( v, vector ), 4.0 );
gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 ) * intensity;
}
</script>
which looks completely different from my original shader even when at the origin, and continues to change appearance as I zoom and pan the scene.
I am at a loss. Does anybody know how to transform the normal vector correctly (or what other change(s) should be made) so that the appearance produced by this shader does not change when zooming/panning the scene?
[Update]
There seems to be a promising lead at http://john-chapman-graphics.blogspot.com/2013/01/good-enough-volumetrics-for-spotlights.html
In particular,
The edges ... need to be softened somehow, and that's where the vertex normals come in. We can use the dot product of the view space normal (cnorm) with the view vector (the normalised fragment position, cpos) as a metric describing how how near to the edge ... the current fragment is.
Does anybody know the code for calculating the view space normal and the view vector?
Figured it out!
In my code above, vNormal
was the vertex normal. To get the view vector, add a uniform to the shader whose value is the difference between the mesh's position and the camera's position. (In theory, the value should be the difference between each vertex's position and the camera's position, but this was close enough for my purposes.)
For an implementation, check out: http://stemkoski.github.io/Three.js/Shader-Glow.html