多Pass渲染

多Pass渲染允许一个物体被渲染多次,每次使用不同的渲染逻辑。本教程通过实现描边效果,学习多Pass渲染的原理和应用。

什么是多Pass渲染

多Pass渲染是指在一次绘制调用中,对同一个物体执行多个渲染通道:

SubShader "Default" {
  Pass "Outline" {
    // 第一个Pass:渲染描边
  }
  
  Pass "Main" {
    // 第二个Pass:渲染物体本身
  }
}

执行顺序:引擎会按照Pass的声明顺序依次执行。

描边效果原理

描边效果通过两个Pass实现:

  1. 描边Pass:渲染放大的背面,产生描边
  2. 主体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渲染的常用应用场景

1. 描边效果

  • 第一个Pass渲染描边
  • 第二个Pass渲染物体本身

2. 阴影渲染

  • 第一个Pass生成阴影贴图
  • 第二个Pass使用阴影贴图渲染

3. 后处理效果

  • 多个Pass实现复杂的后处理链

4. 多层材质

  • 每个Pass渲染不同的材质层

性能考虑

多Pass渲染会增加绘制调用次数:

  • 优点:实现复杂效果,逻辑清晰
  • 缺点:性能开销较大
  • 建议:注意深度写入等渲染状态相关的问题

运行效果

这个着色器会:

  1. 第一个Pass渲染黑色描边(放大的背面)
  2. 第二个Pass渲染正常的纹理物体
  3. 两个Pass叠加产生描边效果
  4. 可以调整描边宽度和颜色

前往 游乐场

这篇文档对您有帮助吗?