GLSL - calculate normal surface

I have a simple vertex shader written in GLSL, and I was wondering if anyone could help me in calculating the normals for the surface. I am β€œupgrading” a flat surface, so the current light model looks ... weird. Here is my current code:

varying vec4 oColor; varying vec3 oEyeNormal; varying vec4 oEyePosition; uniform float Amplitude; // Amplitude of sine wave uniform float Phase; // Phase of sine wave uniform float Frequency; // Frequency of sine wave varying float sinValue; void main() { vec4 thisPos = gl_Vertex; thisPos.z = sin( ( thisPos.x + Phase ) * Frequency) * Amplitude; // Transform normal and position to eye space (for fragment shader) oEyeNormal = normalize( vec3( gl_NormalMatrix * gl_Normal ) ); oEyePosition = gl_ModelViewMatrix * thisPos; // Transform vertex to clip space for fragment shader gl_Position = gl_ModelViewProjectionMatrix * thisPos; sinValue = thisPos.z; } 

Does anyone have any ideas?

+4
source share
2 answers

Well, just take this in terms of differential geometry. You have a parametric surface with s and t parameters:

 X(s,t) = ( s, t, A*sin((s+P)*F) ) 

So, first we calculate the tangents of this surface, being partial derivatives with respect to our two parameters:

 Xs(s,t) = ( 1, 0, A*F*cos((s+P)*F) ) Xt(s,t) = ( 0, 1, 0 ) 

Then we just need to calculate their transverse product to get the normal:

 N = Xs x Xt = ( -A*F*cos((s+P)*F), 0, 1 ) 

So, your normal computer can be calculated completely analytical, you do not need the gl_Normal attribute:

 float angle = (thisPos.x + Phase) * Frequency; thisPos.z = sin(angle) * Amplitude; vec3 normal = normalize(vec3(-Amplitude*Frequency*cos(angle), 0.0, 1.0)); // Transform normal and position to eye space (for fragment shader) oEyeNormal = normalize( gl_NormalMatrix * normal ); 

Normalizing normal may not be necessary (since we normalize the transformed norm anyway), but at the moment I'm not sure that a non-normalized norm should behave correctly if there is uneven scaling. Of course, if you want the normal point to point to the negative z-direction, you need to deny it.


Well, a surface path in space would not be necessary. We can just think of a sine curve inside the xz plane, since the y-part of the normal is zero, since only z depends on x. Thus, we simply take the tangent to the curve z=A*sin((x+P)*F) , whose slope is the derivative of z, which is the xz-vector (1, A*F*cos((x+P)*F)) , the normal to this is simply (-A*F*cos((x+P)*F), 1) (switching coords and negation of one), being x and z (non-normalized) normal. Well, there are no three-dimensional vectors and partial derivatives, but the result is the same.

+6
source

In addition, you should tune your performance:

 oEyeNormal = normalize(vec3(gl_NormalMatrix * gl_Normal)); 
  • There is no need to pass it to vec3 since gl_NormalMatrix is ​​a 3x3 matrix.
  • There is no need to normalize your incoming normal level in your vertex shader, since you do not perform calculations based on length. Some sources claim that incoming normals should always be normalized by the application, so there is no need for an vertex shader. But since this is from the hands of a shader developer, I still normalize them when I calculate the lighting on top (gouraud).
-one
source

Source: https://habr.com/ru/post/1399920/


All Articles