关于cg中的修饰符关键字in out inout的区别, 网上的大致描述如下:

参数传递是指:函数调用实参值初始化函数形参的过程。在 C\C++中,根据形参值的改变是否会导致实参值的改变,参数传递分为“值传递(pass-by-value) ” 和“引用传递(pass-by-reference) ”。按值传递时,函数不会访问当前调用的实参,函数体处理的是实参的拷贝,也就是形参,所以形参值的改变不会影响实参值;引用传递时,函数接收的是实参的存放地址,函数体中改变的是实参的值。

C\C++ 采取指针机制构建引用传递,所以通常引用传递也称为“指针传递”。Cg 语言中参数传递方式同样分为“值传递”和“引用传递”,但指针机制并不被 GPU 硬件所支持,所以 Cg 语言采用不同的语法修辞符来区别“值传递”和“引用传递”。这些修辞符分别为:


in: 修辞一个形参只是用于输入,进入函数体时被初始化,且该形参值的改变不会影响实参值,这是典型的值传递方式。

out: 修辞一个形参只是用于输出的,进入函数体时并没有被初始化,这种类型的形参一般是一个函数的运行结果;

inout: 修辞一个形参既用于输入也用于输出,这是典型的引用传递。

于是写了一段测试程序, 只在红色通道上输出uv的u值

#pragma vertex vert
#pragma fragment frag

struct appdata
{
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
};

struct v2f
{
    float2 uv : TEXCOORD0;
    float4 vertex : SV_POSITION;
};

v2f vert (appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    return fixed4(i.uv.x,0,0,1);
}

在unity里运行可以看到的效果如下:

现在代码作如下修改,

float test(in float x)
{
    x = clamp(x * 2,0,1);
    return x;
}

fixed4 frag (v2f i) : SV_Target
{
    i.uv.x = test(i.uv.x);
    return fixed4(i.uv.x,0,0,1);
}

运行结果:

test() 函数去掉关键字修饰符in, 发现运行效果跟上图一致。

这说明in 是pass with value。 参数是拷贝过去的, 在实际操作的过程中in可以不写。

这时我们把test里的in改为out 发现编译器报错, error内容如下:

variable 'x' used without having been completely initialized

报错提示x没有初始化,于是我们再次修改为test函数如下:

void test(out float x)
{
    x=0.2;
    x = clamp(x * 2,0,1);
}

fixed4 frag (v2f i) : SV_Target
{
    test(i.uv.x);
    return fixed4(i.uv.x,0,0,1);
}

error消除,此时test需要给x一个初始化的值,而且test此时并没有返回值,运行的结果也传递给外部。运行效果如下,

接着测试inout,此时去掉test的x的初始值,作如下修改:

void test(inout float x)
{
    x = clamp(x * 2,0,1);
}

运行结果如下图:

说明inout就是引用,即pass with reference。