众位大哥,小弟想载入法线贴图很久了,但是老是失败,代码也比较凌乱,就不打算贴太多了,直接上图片。我不是太懂为什么一个正方体读入其中会变成这样,我用相同的模型读取一个场景盒是可以的,API和Shader都可以轻松用这个方块做出来,就只有法线贴图不行。同一个模型,读出不同样式!好头痛,有哪位大哥大姐有知道的吗?先在这里谢谢了。

解决方案 »

  1.   

    对了,忘记说了,我的图形库是D3D的,觉得这个应该是顶点着色器的问题。
    struct VS_INPUT 
    {
       float4 Position : POSITION0;
       float2 TexCorrd : TEXCOORD0;
       float3 Normal   : NORMAL0;
       float3 Binormal : BINORMAL0;
       float3 Tangent  : TANGENT0;
       
    };struct VS_OUTPUT 
    {
       float4 Position : POSITION0;
       float2 TexCoord : TEXCOORD0;
       float3 Eye      : TEXCOORD1;
       float3 Light    : TEXCOORD2;
       
    };VS_OUTPUT vs_main( VS_INPUT Input )
    {
       VS_OUTPUT Output;
       float4 OffsetPos = mul(Input.Position,matWorld);   Output.Position = mul( OffsetPos, matViewProjection );
       Output.TexCoord = Input.TexCorrd * 0.5f;
       
       float3 world = mul(Input.Position,matWorld);
       float3 eye = vViewPosition - world; 
       
       Output.Eye.x = dot(Input.Tangent, eye);
       Output.Eye.y = dot(Input.Binormal, eye);
       Output.Eye.z = dot(Input.Normal, eye);
          
       Output.Light.x = dot(Input.Tangent, vLight);
       Output.Light.y = dot(Input.Binormal, vLight);
       Output.Light.z = dot(Input.Normal, vLight);
       return( Output );
       
    }
    这里是顶点着色器的代码,如果有懂的前辈能帮忙指点下错误吗?
    真是谢谢了 T_T
      

  2.   

    难道这个问题很复杂吗?据说法线贴图的算法是比较基础的Shader算法,可能我还连基础的基础都没有掌握吗?
    %>_<%求指点,谢谢了!
      

  3.   

    Output.TexCoord = Input.TexCorrd * 0.5f;为什么标准的纹理坐标要乘以一个0.5?
      

  4.   

      float3 eye = vViewPosition - world; 
       
      Output.Eye.x = dot(Input.Tangent, eye);
      Output.Eye.y = dot(Input.Binormal, eye);
      Output.Eye.z = dot(Input.Normal, eye);还有这儿你的eye向量没有normalize。
      

  5.   

    不知道你的TBN是不是在世界坐标空间下通常我们会把模型空间下的TBN变换到世界坐标系中,然后将TBN传给pixel shader。而不是在vs中做。
      

  6.   

    输入进来的纹理*0.5只是用于测试的,贴图是一个石板,上面石头比较小,这样的方式,可以放大一点。这个东西在RenderMonkey里面是可以运行的额。。至于eye和light都没有标准化,我是在pixel Shader里面标准化的,可能有些习惯不太好。float4 ps_main( PS_INPUT Input ) : COLOR0
    {
          //贴图颜色,环境光和漫反射光照亮这个颜色
          float4 baseColor = tex2D(TextureMap, Input.TexCoord);
       
          float3 light = normalize(Input.Light);
          float3 eye = normalize(Input.Eye);
          float3 normal = normalize(tex2D(NormalMap, Input.TexCoord).xyz * 2.0f - 1.0f);
      

  7.   

    “通常我们会把模型空间下的TBN变换到世界坐标系中,然后将TBN传给pixel shader。而不是在vs中做。”意思是,把模型的TNB弄成一个float3x3的矩阵,然后乘以世界矩阵?然后一起传给像素着色器来处理?
      

  8.   

     float3x3 objToTangentSpace;
       
     objToTangentSpace[0]=T;           
     objToTangentSpace[1]=cross(T, N);    
     objToTangentSpace[2]=N;  
     
     newMat = objToTangentSpace * world;
    这样?
    总觉得怪怪的,这位大哥可以再说得仔细一些吗?我是新手,学习Shader都是自己打拼,没人带,基础有些差。谢谢你了。
      

  9.   

    我贴一段代码给你吧,都是基础,最好你去找找一些最基本的文档来看。关于normal map的,建议你去看microsoft DX8 文档中有一篇很著名的文档,叫per-pixel lighting。是现在很多图形算法的基础。vs:struct VS_INPUT
    {
        float3 Position : POSITION;
        float3 Normal   : NORMAL;
        float3 Tangent  : TANGENT0;
        float3 Binormal : BINORMAL0;
        float2 tOffset  : TEXCOORD0;
    };struct VS_OUTPUT
    {
        float4 Position : POSITION;     // 最终位置(投影空间)
        float2 tOffset  : TEXCOORD0;    // 纹理坐标    float3 f3WorldP : TEXCOORD1;     // 位置  (世界空间)
        float3 f3WorldN : TEXCOORD2;     // 法线  (世界空间)
        float3 f3WorldB : TEXCOORD3;     // 副法线(世界空间)
        float3 f3WorldT : TEXCOORD4;     // 切线  (世界空间)
    };
    VS_OUTPUT VS_Entry( VS_INPUT In )
    {
        // 顶点经过世界变换,视角变换,投影变换
        float4 f4Locate = mul( float4( In.Position, 1.0 ), m4World );
        float4 f4Viewer = mul( f4Locate, m4View );
        
        // 写入数据
        VS_OUTPUT Out = (VS_OUTPUT)0;
        Out.Position    = mul( f4Viewer, m4Proj );
        Out.tOffset     = In.tOffset;
        
        // 计算世界坐标空间的位置,法线,副法线,切线;
        Out.f3WorldP    = f4Locate;
        Out.f3WorldN    = normalize( mul( In.Normal,   (float3x3)m4World ) );
        Out.f3WorldB    = normalize( mul( In.Binormal, (float3x3)m4World ) );
        Out.f3WorldT    = normalize( mul( In.Tangent,  (float3x3)m4World ) );    // 返回;
        return Out;
    }
    ps:float4 PS_Entry( VS_OUTPUT In ) : COLOR
    {
        // 计算视线(世界空间,未单位化);
        float3 f3View = f4EyePosition.xyz - In.f3WorldP;
        
        // 准备切线空间到世界空间的矩阵;
    float3x3 f3x3TBN = float3x3(In.f3WorldT, In.f3WorldB, In.f3WorldN);

    // 计算像素法线(切线空间);
    float3 f3Normal = CalculateNormal(normalMap, In.tOffset);

    // 计算像素法线(世界空间);通过TBN的逆矩阵将切线空间内的法线变换到世界空间;
    f3Normal = normalize( mul( f3Normal, transpose(f3x3TBN) ) );

        // 计算环境光;
    float4 f4AmbColor = f4MaterialAmbient * f4AmbientLight;
        
        // 计算光的反方向;
        float3 lgtDir, eyeDir;
        lgtDir = normalize( -f4DirLightWorldDirection.xyz );
        
        // 计算漫反射强度;
    float fDiffuseScale = saturate( dot(f3Normal, lgtDir) );

    // 计算漫反射;
    float4 f4DifColor = ( f4MaterialAmbient * f4DirLightAmbient + fDiffuseScale * f4MaterialDiffuse * f4DirLightDiffuse );
        
        // 计算高光向量
        eyeDir = normalize( f3View );    
        lgtDir = normalize( In.f3WorldP - f4DirLightWorldPosition.xyz );
    float3 fReflect = normalize( reflect(lgtDir, f3Normal) );

    // 计算高光的最终值
    float4 f4SpcColor = f4DirLightSpecular * f4MaterialSpecular * ( pow( saturate( dot(fReflect, eyeDir) ), fSpecularPower ) ) * fDiffuseScale;

    // 最终的光颜色
    float4 f4Final = f4MaterialEmissive + f4AmbColor + f4DifColor + f4SpcColor;

    // 计算最终的光照;
    In.Color = f4Final * float4( co.rgb, 1.0f );
    }以上代码是从项目中提取出来的,你要测试的话,需要自己该一些东西。这些代码就是最基本的per-pixel lighting的光源模型。
      

  10.   

    哎呀呀,真是太感谢你了!其实我写了一小段时间的Shader,但是,只会写一些平面整体渲染类型的,一遇到矩阵之类的就非常头痛。谢谢你的指点!加你好友了,谢谢,虽然我技术很薄弱,但有什么能帮上忙的就说,我尽力!
      

  11.   

    9L的代码有问题额。// 计算像素法线(世界空间);通过TBN的逆矩阵将切线空间内的法线变换到世界空间;
    f3Normal = normalize( mul( f3Normal, transpose(f3x3TBN) ) );这里的TBN矩阵就是将切线空间法线变换到世界空间(T,B,N三个基都是处于世界空间),逆矩阵是将世界空间法线变换到切空间。。