THREE.JS GLSL sprite always front to camera

2020-05-04 07:06发布

问题:

I'm creating a glow effect for car stop lights and found a shader that makes it possible to always face the camera:

uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;
void main() {
    vec3 vNormal = normalize( normalMatrix * normal );
    vec3 vNormel = normalize( normalMatrix * -viewVector );
    intensity = pow( c - dot(vNormal, vNormel), p );
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

This solution is quite simple and almost works. It reacts to camera movement and it would be great. BUT this element is a child of a car. The car itself is moving around and when it rotates the material stops pointing directly at the camera.

I don't want to use SpritePlugin or LensFlarePlugin because they slow down my game by 20fps so I'll stick to this lightweight solution.

I found a solution for Direct 3d that you have to remove rotation data from tranformation matrix, but I don't know how to do this in THREE.js

I guess that instead of adding calculations with car transformation there must be a way to simplify this shader instead.

How to simplify this shader so the material always faces the camera?

From the link below: "To do spherical billboarding, just remove all rotations by setting the identity matrix". How to do it ShaderMaterial in THREE.js? http://www.geeks3d.com/20140807/billboarding-vertex-shader-glsl/

The problem here I think is intercepting transformation matrix from ShaderMaterial before it's passed to the shader, but I'm not sure.

Probably irrelevant but here's also fragment shader:

uniform vec3 glowColor;
varying float intensity;
void main() {
    vec3 glow = glowColor * intensity;
    gl_FragColor = vec4( glow, 1.0 );
}

edit: for now I found a workaround which is eliminating parent's rotation influence by setting opposite quaternion. Not perfect and it's happening in CPU not GPU

this.quaternion._x = -this.parent.quaternion._x;
this.quaternion._y = -this.parent.quaternion._y;
this.quaternion._z = -this.parent.quaternion._z;
this.quaternion._w = -this.parent.quaternion._w;

回答1:

Are you looking for an implementation of billboarding? (make a 2D sprite always face camera) If so, all you need to do is this:

"vec3 billboard(vec2 v, mat4 view){",
    "   vec3 up = vec3(view[0][1], view[1][1], view[2][1]);",
    "   vec3 right = vec3(view[0][0], view[1][0], view[2][0]);",
    "   vec3 p = right * v.x + up * v.y;", 
    "   return p;",
"}"

v is the offset from the center, basically the 4 vertices in a plane that faces the z-axis. Eg. (1.0, 1.0), (1.0, -1.0), (-1.0, 1.0), and (-1.0, -1.0).

Use it like so:

"vec3 worldPos = billboard(a_offset, u_view);"
// then do whatever else.