【Unity】Compute Shader计算BRDF存储到纹理

在移动平台,BRDF的计算会消耗较多的ALU,同时对于BRDF计算结果的精度要求没有这么高。一种优化的方式是将BRDF的部分复杂计算,预计算存储到纹理中。已增加一张BRDF贴图和纹理采样器的代价,来节约ALU。

将BRDF预计算存储到纹理的方式有很多,使用C#实现或者Unity2018的新的Custom Render Texture都可以很方便的实现功能。

作为一个Compute Shader的入门,这里使用Compute Shader,也就是计算着色器来实现我们的需求。

首先,我们新建一个C#脚本,BRDFGenerator.cs

 1 using UnityEditor;
 2 using UnityEngine;
 3 using System.Collections;
 4 using System.Collections.Generic;
 5 public class BRDFGenerator : ScriptableWizard
 6 {
 7     public ComputeShader computeShader;
 8     public int size=256;
 9  //   public RenderTexture rt;
10     public Material mat;
11     private int _kernel;
12     [MenuItem("Texture Generator/BRDF Texture Generator")]
13     static void CreateWizard()
14     {
15     //    ScriptableWizard.DisplayWizard<t>("Create Light", "Create", "Apply");
16         ScriptableWizard.DisplayWizard<BRDFGenerator>("BRDF Texture Generator", "生成");
17         //If you don't want to use the secondary button simply leave it out:
18     }
19     private void OnEnable()
20     {
21         Debug.Log("Start!");
22     }
23     void OnWizardCreate()
24     {
25         RenderTexture renderTexture = new RenderTexture(size,size,0,RenderTextureFormat.ARGB32);
26         renderTexture.enableRandomWrite = true;
27         renderTexture.Create();
28      //   rt.enableRandomWrite = true;
29         mat.SetTexture("_MainTex", renderTexture);
30         float temp = size / 1.0f;
31         _kernel = computeShader.FindKernel("CSMain");
32         computeShader.SetTexture(_kernel, "Result", renderTexture);
33         computeShader.SetFloat("size", temp);
34         computeShader.Dispatch(_kernel, renderTexture.width / 8, renderTexture.height / 8, 1);
35         Debug.Log("Sucess!");
36     }
37 
38     void OnWizardUpdate()
39     {
40         helpString = "BRDF";
41     }
42 
43 }
BRDFGenerator

BRDFGenerator.cs实现了一个窗口

 同时,还进行了线程的分发,这里,我们一个线程控制一个像素点。

对应的Compute Shader如下:

 1 // Each #kernel tells which function to compile; you can have many kernels
 2 #pragma kernel CSMain
 3 
 4 // Create a RenderTexture with enableRandomWrite flag and set it
 5 // with cs.SetTexture
 6 RWTexture2D<float4> Result;
 7 float size;
 8 #define UNITY_PI            3.14159265359f
 9 #define UNITY_INV_PI        0.31830988618f
10 float Pow5(float x)
11 {
12     return x*x*x*x*x;
13 }
14 [numthreads(8,8,1)]
15 void CSMain (uint3 id : SV_DispatchThreadID)
16 {
17     // TODO: insert actual code here!
18     float x=id.x/size;
19     float y=id.y/size;
20     //可见性项计算
21     float roughness=x;
22     float r2=roughness*roughness;
23     float dot2=y*y;
24     float V=   (-1 + sqrt(r2 * (1 - dot2) / dot2 + 1)) * 0.5f;
25     //微表面分布项计算
26     float nh=y;
27     float d_temp = (nh * r2 - nh) * nh + 1.0f; 
28        float D= UNITY_INV_PI * r2 / (d_temp * d_temp + 1e-7f); 
29     //菲涅尔计算
30     float cosA=x;
31     float F=Pow5(1-cosA);
32     //漫反射计算
33     float fd90=y*2.5;
34     float diffuse   = (1 + (fd90 - 1) * Pow5(1 - x))*0.4;
35     //输出结果
36     Result[id.xy] = float4(V, D, F, diffuse);
37 }
Compute Shader

Compute Shader的语法和顶点及片段着色器没有什么区别,SV_DispatchThreadID的具体解释可以查阅MSDN。

这里使用的BRDF的计算都是摘取的UnityStandardBRDF.cginc的实现,也可以选择自己的BRDF模型。

BRDF的各项分别存储在RGBA四个通道中。计算速度比在C#中快超多。

结果如下:

RGB通道

Alpha通道

posted @ 2017-08-18 14:27  JaffHan  阅读(1857)  评论(0编辑  收藏  举报