UnityComputeShader Challenge1
using UnityEngine;
using System.Collections;public class Challenge1 : MonoBehaviour
{//editor拖拽对应的 compute文件public ComputeShader shader;//纹理的大小public int texResolution = 1024;//着色器Renderer rend;//用于存储计算输出RenderTexture outputTexture;int kernelHandle;// Use this for initializationvoid Start(){//创建纹理,width,height,depthoutputTexture = new RenderTexture(texResolution, texResolution, 0);//是否允许随机读写outputTexture.enableRandomWrite = true;outputTexture.Create();rend = GetComponent<Renderer>();rend.enabled = true;InitShader();}private void InitShader(){ //获得compute 中的kernel 函数kernelHandle = shader.FindKernel("Square");int halfRes = texResolution >> 1;int quarterRes = texResolution >> 2;//矩形盒的两个点Vector4 rect = new Vector4( quarterRes, quarterRes, halfRes, halfRes );//设置参数shader.SetVector( "rect", rect );shader.SetTexture(kernelHandle, "Result", outputTexture);//将输出的材质应用到当前组件上,editor中设置对应meshrend.material.SetTexture("_MainTex", outputTexture);//分发任务,启动对象的线程组数量,这里为什么是8 在compute文件中通过[numthreads(8, 8, 1)] 设置了一组线程组的大小,8 8 1分别表示x方向上8个线程,y方向上8个z方向上1,总计64个线程。因为对应的texture x,y,z 分别是 1024,1024,1 为了覆盖所有的像素,就需要 1024/64=8,这就是为什么这里是8的原因DispatchShader(texResolution / 8, texResolution / 8);}private void DispatchShader(int x, int y){shader.Dispatch(kernelHandle, x, y, 1);}void Update(){ //这里没看懂干嘛的if (Input.GetKeyUp(KeyCode.U)){DispatchShader(texResolution / 8, texResolution / 8);}}
}
// Each #kernel tells which function to compile; you can have many kernels
// 告诉编译器,那些函数是内核函数
#pragma kernel Square// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
// 声明可读可写的属性,
RWTexture2D<float4> Result;
//float4 32bit*4 我的理解方式是float 四元组
float4 rect;
//判定是否在矩形内,这里判定还是蛮有意思的,可以使用非if else的形式确定,点是否在矩形内,由于线程组内使用的程序计数器是统一的,所以if else会导致,不符合的线程等待符合的线程,从而大幅降低线程性能,而这种方式可以确保所有线程并行效率,比如点a,点b a属于矩形内,b不属于矩形内。当进行判定的时候如果使用if else,则线程组内需要执行两次调度,但是下面的方式可以并行执行,只需要一次
float inSquare( float2 pt, float4 rect ){//补充说明step函数,很好理解,第一个参数是一个阶梯,第二个参数如果在阶梯的上面则返回1,否则返回0。上面的意思是数值更大,通常用于去除if else float horz = step( rect.x, pt.x ) - step( rect.x + rect.z, pt.x );//一个点是否在一个线段上(a,b),先判断x是否大于a,再排除掉x>b的情况float vert = step( rect.y, pt.y ) - step( rect.y + rect.w, pt.y );return horz * vert;//仅当x轴和y轴满足条件 即 1*1=1 || -1*-1 才会返回 1,其余条件都是0
}
//生命线程组大小, id 为 unit3元组,分辨表示,当前线程的位置,一个不恰当的理解方式就是该像素,处于纹理的坐标假设像素是一个个的小颗粒,小颗粒的长宽高分别都是1
[numthreads(8,8,1)]
void Square (uint3 id : SV_DispatchThreadID)
{//这里参数是否意味着,inSquare内上下文是拿不到 rect的//验证后发现 及时不传递rect,还是可以有正确的表现float res = inSquare( (float2)id.xy, rect );Result[id.xy] = float4(0.0, 0.0, res, 1.0);
}
运行效果