Material proxies

From Valve Developer Community
< Zh
Jump to navigation Jump to search
English (en)Português do Brasil (pt-br)中文 (zh)Translate (Translate)
Info content.png
This page needs to be translated.
This page either contains information that is only partially or incorrectly translated, or there isn't a translation yet.
If this page cannot be translated for some reason, or is left untranslated for an extended period of time after this notice is posted, the page should be requested to be deleted.
Also, please make sure the article tries to comply with the alternate languages guide.

使用ChatGPT翻译

材质代理允许游戏的编译C++代码操作材质的属性。许多代理执行特定任务,但也有其他更通用的代理,它们共同在VMT文件中提供基本的脚本支持。[澄清]

可以向材质添加任意数量的代理,它们会按照出现的顺序执行。

Icon-Bug.png错误:一些用户报告说工具模式下某些功能代理在游戏中无法运行。  [todo tested in?]
待完善: 该bug的范围尚未测试。
Icon-Bug.png错误:使用某些材质代理在动态创建的粒子上时,例如env_smokestack,会导致游戏在加载地图时崩溃。  [todo tested in?]

用法

该材质有一个 Sine 代理,使其在8秒内逐渐显现和消失

LightmappedGeneric
{
	$basetexture shadertest/LightmappedTexture

	Proxies // 代理列在此块内
	{
		Sine // 产生正弦波的代理
		{
			resultVar	$alpha // 要操作的着色器参数
			sineperiod	8
			sinemin		0
			sinemax		1
		}
	}
}

变量

材质可以声明自己的变量供内部使用。这些变量必须在Proxies块之外声明,在材质的主体部分,并且必须指定默认值。

这些自定义变量可能用于在代理之间传递结果或向其提交硬编码的数据。它们通常用于将数学函数代理(例如 AddSubtract 等)串联成更长的方程式。对于2D/3D/4D向量("[0 0 0]"),变量名称可以附加[0]以读取/写入特定索引。写入索引变量时应将其包围在引号中。

引擎使用8位有符号整数对这些自定义变量进行编码。因此,每个材质最多支持128个唯一自定义变量。

以下示例通过错开正弦波的起始位置来扩展上述示例:

LightmappedGeneric
{
	$basetexture shadertest/LightmappedTexture

	$offset 0 // 声明自定义变量
	
	Proxies
	{
		EntityRandom
		{
			resultVar $offset // 写入自定义变量
		}
		Sine
		{
			resultVar	$alpha
			timeoffset	$offset // 从自定义变量读取
			sineperiod	8
			sinemin		0
			sinemax		1
		}
	}
}

现在,每个使用该材质的实体都会按照自己的时间表脉冲。

写入索引变量的其他示例:

	$color "[0 0 0]"

	proxies
	{
		sine
		{
			sineperiod	1.3
			sinemin		0
			sinemax		1
			timeoffset	0
			resultvar	"$color[0]"
		}
		sine
		{
			sineperiod	1.7
			sinemin		0
			sinemax		1
			timeoffset	0
			resultvar	"$color[1]"
		}
		sine
		{
			sineperiod	2.3
			sinemin		0
			sinemax		1
			timeoffset	0
			resultvar	"$color[2]"
		}
	}

使用颜色向量创建“随机”颜色脉冲的示例:

UnlitGeneric
{
	$basetexture "dev\gradient_dif"
	$color "[1 .8 .6]"

	$detail "dev\noise_512x32"
	$detailscale 1
	$detailblendmode 0
	$detailblendfactor 4.0

	$additive 1
	$nocull 1

	$cvar "[.5 .5]"
	$svar "[1 .25]"
	$rvar 0
	$tvar "[0 0]"

	$sine1 0
	$sine2 0

	proxies
	{
		linearramp
		{
			rate .3
			initialvalue 0
			resultvar "$tvar[1]"
		}
		sine
		{
			sineperiod 1.3
			sinemin -.004
			sinemax .002
			timeoffset 0
			resultvar $sine1
		}
		sine
		{
			sineperiod 1.7
			sinemin -.003
			sinemax .007
			timeoffset .2
			resultvar $sine2
		}
		add
		{
			srcvar1 $sine1
			srcvar2 $sine2
			resultvar "$tvar[0]"
		}
		texturetransform
		{
			centervar $cvar
			scalevar $svar
			rotatevar $rvar
			translatevar $tvar
			resultvar $detailtexturetransform
		}
	}
}

动态纹理变换的示例。

分割向量

使用向量时有些特殊之处。并非所有代理都能识别向量分量。如果需要单独处理向量的各个分量,则必须先将它们拆分成不同的变量。 所有代理中的resultVar都会识别向量分量。

以下代理和仅这些关键字可以识别向量分量:

  • Clamp: min, max
  • Sine: offset, max, min, period
  • LinearRamp: rate, initial value
  • UniformNoise: min, max
  • GaussianNoise: min, max, mean, halfwidth
  • WrapMinMax: min, max
  • Exponential: min, max, scale, offset

上述所有代理都可以用于分割向量。 但是Clamp是最便宜的选择:

        $pos "[0 0 0]"
        $posX .0        //必须为浮点数,否则Clamp无法正确保存值
        $posY .0        //必须为浮点数,否则Clamp无法正确保存值
        $posZ .0        //必须为浮点数,否则Clamp无法正确保存值
        
        $zero 0
        
        //输出3D向量的代理
        PlayerPosition
        {
                scale                    1
                resultVar               "$pos"
        }
        
        //拆分3D向量以便进一步使用
        Clamp
        {
            srcVar1                      $zero
            min                         "$pos[0]"
            max                         "$pos[0]"
            resultVar                    $posX
        }
        
        Clamp
        {
            srcVar1                      $zero
            min                         "$pos[1]"
            max                         "$pos[1]"
            resultVar                    $posY
        }
        
        Clamp
        {
            srcVar1                      $zero
            min                         "$pos[2]"
            max                         "$pos[2]"
            resultVar                    $posZ
        }
Warning.png警告:引用时需要加引号,以便正确处理向量分量或定义向量。

编写新的代理

创建新的代理很简单。它们仅存在于客户端,并应继承自IMaterialProxy或其子类。

你需要这些头文件:

  • "materialsystem/IMaterialProxy.h"
  • "materialsystem/IMaterialVar.h"

以下函数包含在接口中:

bool Init( IMaterial* pMaterial, KeyValues* pKeyValues )
在材质首次预加载时调用。使用此函数初始化变量并获取你将使用的材质变量的引用。成功时返回true,失败时返回false(此时代理将不会执行)。
pKeyValues包含来自VMT文件的代理参数。
void OnBind( void* pC_BaseEntity )
在材质即将应用到实体进行渲染时调用。这里是执行工作的地方。
编写此函数时需要记住,所有使用同一材质的实体共享同一个材质对象,若在一个实体上修改了材质,它将在其他所有实体上也发生变化。由于OnBind()会在每次渲染实体时被调用,只要每次都重新分配你想要的值就不会有问题。不要因为没有变化而提前返回,也不要在代理中存储输入数据。
Note.png注意:pC_BaseEntity并不会指向C_BaseEntity,而是指向关联的IClientRenderable。要直接访问实体,最简单的方法是基于CEntityMaterialProxy(位于proxyentity.h)并使用其提供的OnBind(C_BaseEntity*)重载。
void Release()
待完善: 代理被移除时调用,但是什么时候呢?
IMaterial* GetMaterial()
获取代理所附加的材质。
Tip.png提示:如果你有一个材质变量存储,你可以返回IMaterialVar::GetOwningMaterial(),而不是创建一个新的IMaterial指针。

接口

代理必须通过EXPOSE_INTERFACE宏向材质暴露其接口:

EXPOSE_INTERFACE( <className>, <interfaceName>, "<proxyName>" IMATERIAL_PROXY_INTERFACE_VERSION );

代理名称和接口版本之间没有逗号,这是故意的。

工具录制

以下代码已被添加到所有代理中,适用于Orange Box:

#include "toolframework_client.h"

void OnBind(...)
{
	//...

	if ( ToolsEnabled() )
		ToolFramework_RecordMaterialParams( GetMaterial() );
}

它可能与Source Filmmaker相关。最好在你的代理中也添加这段代码,以防Filmmaker发布!

Tip.png提示:CEntityMaterialProxy会自动进行此调用。

另见

外部链接