» cn.Tutorial Effects - Normals
This site relies heavily on Javascript. You should enable it if you want the full experience. Learn more.

cn.Tutorial Effects - Normals

English | Italian | Japanese

TOC: Of Effects and Shaders
Back: Vertex Data
Next: Geometry Morphing


除了顶点位置和材质坐标,面法线是第三个重要且常用的顶点数据字段。法线是在顶点位置垂直于表面曲率的一个3D向量。当灯光向量被这个表面反射要获取这个表面着色的时候,法线是对于光线的计算是十分重要的。

显示法线

VVVV有一个帮助的模版来显示模型网格上的面法线:Normals (EX9)

我们能够修改这个帮助的程序片来使模型网格上的面法线更好的显示出来:

计算法线

在你把模型网格传递到着色器之前,如果你的模型网格没有储存法线你可以使用Normals (EX9.Geometry Mesh)这个节点在程序片中计算出来使用Smoothing Angle引脚在顶点位置平均化法线。

因为大多数的着色模型(如phone)在视点空间中需要法线,它得随着顶点的位置变换法线。但是你要注意法线是一个3位浮点数,如果它是4位浮点数的话,那法线上的第4位w元素的是0(而不是对于位置来说的1),这个元素仅获取旋转的值,而不是移动值。一个典型的顶点着色器做如下的转换:

...
    //normal in view space
    Out.NormV = normalize(mul(NormO, tWV));
 
    //position (projected)
    Out.PosWVP  = mul(PosO, tWVP);
    ...

重新计算法线

像我们在"抖动先生“函数打印这两个教程里一样,如果你在顶点着色其中改变顶点的位置这会变得更加复杂。因为顶点的位置移动改变了表面的曲率,前面在模型网格中储存的法线将不在有用。因此着色计算就会出错。

因此,如果你需要在像素着色器中计算灯光,你就必须找到一个方法从新计算法线。但是这是一个十分具体的问题,它取决于你移动顶点位置使用的方法。

大多数通常的情况,不是如此精确的方法去获得新的顶点法线的位置是计算另外两个附近的点,获得两个向量,它们的正切面与这个顶点不是平行的。从这个两个正切的向量你能够通过叉积计算法线的位置:

  • calc current pos p
  • calc neighbour pos n1
  • calc neighbour pos n2
  • get tangent vector t1 = p - n1
  • get tangent vector t2 = p - n2
  • get normal n = t1 x t2

使用圆锥的例子函数打印如下:

float NeighbourOffset = 0.001;
 
vs2ps VS(
    float4 PosO  : POSITION,
    float4 TexCd : TEXCOORD0)
{
    //declare output struct
    vs2ps Out;
 
    //calc new position
    float3 p = Cone(PosO.xy);
 
    //calc neighbour pos in u direction
    float3 n1 = Cone(PosO.xy + float2(NeighbourOffset, 0));
 
    //calc neighbour pos in v direction
    float3 n2 = Cone(PosO.xy + float2(0, NeighbourOffset));
 
    //get tangent vector 1
    float3 t1 = p - n1;
 
    //get tangent vector 2
    float3 t2 = p - n2;
 
    //get normal
    float3 NormO = cross(t1, t2);
 
    //set new pos
    PosO.xyz = p;
    ...

现在,为了实际的使用法线我们可以以Phong函数为例,这个可以在vvvv的effects文件夹PhongDirectional.fxh文件中找到。为此拷贝这个文件除了你的着色器,然后看一看PhongDirectional.fx着色器,去提取必要的命令行使得phone着色器能够运行。我们需要把法线,灯光,视角都放进视点空间,让后把它们在vs2ps结构中都传递到像素着色器里。让我们同样拷贝材质的代码去得到一个很好的着色设定,这里你仅仅需要使用所有的uv-参数表面(你可以在网找到)替换圆锥的函数:

// ----------------------------------------------------------------------------
// PARAMETERS:
// ----------------------------------------------------------------------------
 
//transforms
float4x4 tW: WORLD;        //the models world matrix as via the shader
float4x4 tV: VIEW;         //view matrix as set via Renderer (EX9)
float4x4 tP: PROJECTION;   //projection matrix as set via Renderer (EX9)
float4x4 tWV : WORLDVIEW;
float4x4 tWVP: WORLDVIEWPROJECTION; //all 3 premultiplied 
 
//texture transformation marked with semantic TEXTUREMATRIX
//to achieve symmetric transformations
float4x4 tTex: TEXTUREMATRIX <string uiname="Texture Transform";>;
 
//texture
texture Tex <string uiname="Texture";>;
sampler Samp = sampler_state    //sampler for doing the texture-lookup
{
    Texture   = (Tex);          //apply a texture to the sampler
    MipFilter = LINEAR;         //sampler states
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};
 
//the data structure: "vertexshader to pixelshader"
//used as output data of the VS function
//and as input data of the PS function
struct vs2ps
{
    float4 Pos  : POSITION;
    float2 TexCd : TEXCOORD0;
    float3 LightDirV: TEXCOORD1;
    float3 NormV: TEXCOORD2;
    float3 ViewDirV: TEXCOORD3;
};
 
#include "PhongDirectional.fxh"
 
// ----------------------------------------------------------------------------
// VERTEXSHADERS
// ----------------------------------------------------------------------------
 
 
#define twopi 6.28318531
 
float2 Scale = 1;
float2 Offset = 0;
 
float3 Cone(float2 uv)
{
 
     uv *= Scale;
     uv += Offset;
 
    float u = uv.x * twopi;
    float v = uv.y;
 
    float3 newPos;
    newPos.x = v * cos(u);
    newPos.y = v * sin(u);
    newPos.z = v;
 
    return newPos;
}
 
float NeighbourOffset = 0.001;
 
vs2ps VS(
    float4 PosO  : POSITION,
    float4 TexCd : TEXCOORD0)
{
    //declare output struct
    vs2ps Out;
 
    //calc new position
    float3 p = Cone(PosO.xy);
 
    //calc neighbour pos in u direction
    float3 n1 = Cone(PosO.xy + float2(NeighbourOffset, 0));
 
    //calc neighbour pos in v direction
    float3 n2 = Cone(PosO.xy + float2(0, NeighbourOffset));
 
    //get tangent vector 1
    float3 t1 = p - n1;
 
    //get tangent vector 2
    float3 t2 = p - n2;
 
    //get normal
    float3 NormO = cross(t1, t2);
 
    //set new pos
    PosO.xyz = p;
 
    //put normal in view space and normalize it
    Out.NormV = normalize(mul(NormO, tWV));
 
    //inverse light direction in view space
    Out.LightDirV = normalize(-mul(lDir, tV));
 
    //inverse view dir in view space
    Out.ViewDirV = -normalize(mul(PosO, tWV));
 
    //transform position (projected)
    Out.Pos = mul(PosO, tWVP);
 
    //transform texturecoordinates
    Out.TexCd = mul(TexCd, tTex);
 
    return Out;
}
 
// ----------------------------------------------------------------------------
// PIXELSHADERS
// ----------------------------------------------------------------------------
 
float Alpha <float uimin=0.0; float uimax=1.0;> = 1;
 
float4 PS(vs2ps In) : COLOR
{
    float4 col = tex2D(Samp, In.TexCd);
 
    col.rgb *= PhongDirectional(In.NormV, In.ViewDirV, In.LightDirV);
    col.a *= Alpha;
 
    return col;
}
 
// ----------------------------------------------------------------------------
// TECHNIQUES:
// ----------------------------------------------------------------------------
 
technique TFunctionWithPhong
{
    pass P0
    {
        VertexShader = compile vs_1_1 VS();
        PixelShader  = compile ps_2_0 PS();
    }
}

TOC: Of Effects and Shaders
Back: Vertex Data
Next: Geometry Morphing

anonymous user login

Shoutbox

~3d ago

joreg: vvvvTv S02E01 is out: Buttons & Sliders with Dear ImGui: https://www.youtube.com/live/PuuTilbqd9w

~10d ago

joreg: vvvvTv S02E00 is out: Sensors & Servos with Arduino: https://visualprogramming.net/blog/2024/vvvvtv-is-back-with-season-2/

~10d ago

~11d ago

fleg: hey there! What's the best tool for remote work? Teamviewer feels terrible. Thanks!

~24d ago

joreg: Last call: 6-session vvvv beginner course starting November 4: https://thenodeinstitute.org/courses/ws24-5-vvvv-beginners-part-i/

~1mth ago

joreg: Missed the last meetup? You can rewatch it here: https://www.youtube.com/live/MdvTa58uxB0?si=Fwi-9hHoCmo794Ag

~1mth ago

theurbankind: When is the next big event, like node festival ?

~1mth ago

~1mth ago

joreg: Join us for the next vvvv meetup on Oktober 17th: https://visualprogramming.net/blog/2024/25.-vvvv-worldwide-meetup/

~2mth ago

joreg: 6 session beginner course part 2 "Deep Dive" starts January 13th: https://thenodeinstitute.org/courses/ws24-5-vvvv-beginners-part-ii/