多Pass渲染允许一个物体被渲染多次,每次使用不同的渲染逻辑。本教程通过实现描边效果,学习多Pass渲染的原理和应用。
多Pass渲染是指在一次绘制调用中,对同一个物体执行多个渲染通道:
SubShader "Default" {
  Pass "Outline" {
    // 第一个Pass:渲染描边
  }
  
  Pass "Main" {
    // 第二个Pass:渲染物体本身
  }
}执行顺序:引擎会按照Pass的声明顺序依次执行。
描边效果通过两个Pass实现:
Shader "Tutorial/04-Outline" {
  SubShader "Default" {
    // Pass 1: 描边Pass - 先渲染放大的背面
    Pass "Outline" {
      // 渲染状态:只渲染正面,用于描边
      RasterState customRasterState {
        CullMode = CullMode.Front;  // 剔除正面,只渲染背面
      }
      
      DepthState customDepthState {
        WriteEnabled = true;
        CompareFunction = CompareFunction.LessEqual;
      }
 
      RasterState = customRasterState;
      DepthState = customDepthState;
 
      
      mat4 renderer_MVPMat;
      vec4 material_OutlineColor;
      float material_OutlineWidth;
      
      struct a2v {
        vec4 POSITION;
        vec3 NORMAL;
      };
      
      VertexShader = outlineVert;
      FragmentShader = outlineFrag;
      
      // 描边顶点着色器:沿法线方向扩展顶点
      void outlineVert(a2v input) {
        // 将顶点沿法线方向外扩
        vec4 pos = input.POSITION;
        pos.xyz += input.NORMAL * material_OutlineWidth;
        
        gl_Position = renderer_MVPMat * pos;
      }
      
      // 描边片元着色器:输出描边颜色
      void outlineFrag() {
        gl_FragColor = material_OutlineColor;
      }
    }
    
    // Pass 2: 主体Pass - 渲染物体本身
    Pass "Main" {
      // 渲染状态:正常渲染背面
      RasterState customRasterState {
        CullMode = CullMode.Back;   // 剔除背面,渲染正面
      }
      
      DepthState customDepthState {
        WriteEnabled = true;
        CompareFunction = CompareFunction.LessEqual;
      }
      
      RasterState = customRasterState;
      DepthState = customDepthState;
 
      mat4 renderer_MVPMat;
      vec4 material_BaseColor;
      vec3 camera_Position;
      
      struct a2v {
        vec4 POSITION;
        vec3 NORMAL;
      };
      
      struct v2f {
        vec3 worldNormal;
        vec3 worldPos;
      };
      
      VertexShader = mainVert;
      FragmentShader = mainFrag;
      
      // 主体顶点着色器
      v2f mainVert(a2v input) {
        v2f output;
        
        gl_Position = renderer_MVPMat * input.POSITION;
        
        // 传递世界空间法线和位置(简化处理)
        output.worldNormal = input.NORMAL;
        output.worldPos = input.POSITION.xyz;
        
        return output;
      }
      
      // 主体片元着色器:简单的Lambert光照
      void mainFrag(v2f input) {
        vec3 normal = normalize(input.worldNormal);
        vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0)); // 固定光源方向
        
        // Lambert漫反射
        float NdotL = max(dot(normal, lightDir), 0.0);
        vec3 diffuse = material_BaseColor.rgb * NdotL;
        
        // 添加环境光
        vec3 ambient = material_BaseColor.rgb * 0.3;
        
        gl_FragColor = vec4(diffuse + ambient, material_BaseColor.a);
      }
    }
  }
}多Pass渲染会增加绘制调用次数:
这个着色器会:
前往 游乐场