材质代理

It is not recommended to use machine translation without any corrections.
If the article is not corrected in the long term, it will be removed.
Also, please make sure the article complies with the alternate languages guide.
This notice is put here by LanguageBar template and if you want to remove it after updating the translation you can do so on this page.
材质代理允许游戏的编译后C++代码在运行时操作材质 的属性。它们可用于创建动态或动画纹理。
使用方法
材质代理可以通过VMT 文件添加到材质中,具体位于名为Proxies
的KeyValues 块内;其中的每个条目即为我们所称的代理。一个材质可以添加任意数量的代理,代理按出现顺序依次执行。游戏会以极快的速度不断重复执行材质代理列表。[每帧执行?]
代理的名称决定了其拥有的参数和执行的功能。 许多代理执行特定任务,但也有一些更通用的代理共同为VMT文件提供基础脚本支持。[澄清]
通常,每个代理都有一个输出值(例如浮点数 ),该值将被写入其resultVar
,后者可以是着色器参数或变量(变量参见下文)。
例如,以下材质使用Sine
代理生成浮点数并写入材质的$alpha着色器参数,使纹理在8秒周期内渐隐渐现:
LightmappedGeneric
{
$basetexture shadertest/LightmappedTexture
Proxies // 代理在此块内列出
{
Sine // 生成正弦波值的代理
{
sineperiod 8 // 正弦周期长度(秒)
sinemin 0 // 最小值
sinemax 1 // 最大值
resultVar $alpha // 振荡输出值的写入位置
}
}
}
变量
材质可以声明自定义变量供内部使用。此类变量必须在Proxies
块外的材质主体中声明,并需在右侧指定默认值。
这些自定义变量可用于在代理间传递结果或向其提交硬编码数据。它们常被用于将数学函数代理(如Add
、Subtract
等)链接成更长的方程式。对于2D/3D/4D向量($vec "[0 0 0]"
),变量名可附加[0]
("$vec[0]"
)以读写特定索引。对索引变量的写入操作需用引号包裹。
引擎使用8位有符号整数编码这些自定义变量,因此每个材质最多有128个独立自定义变量。
此示例扩展了前例,通过EntityRandom
代理生成的随机值错开正弦波的起始位置:
LightmappedGeneric
{
$basetexture shadertest/LightmappedTexture
$offset 0 // 声明自定义变量($offset不是游戏已知的着色器参数)
Proxies
{
EntityRandom // 生成随机数
{
resultVar $offset // 写入自定义变量
}
Sine
{
resultVar $alpha
timeoffset $offset // 从自定义变量读取
sineperiod 8
sinemin 0
sinemax 1
}
}
}
现在使用此材质的每个实体都会按自身节奏脉动。
以下是通过$color向量写入索引变量创建"随机"颜色脉动的示例:
$color "[0 0 0]" // 非自定义变量,游戏识别"$color"
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
}
}
}
|
拆分向量
使用向量存在特殊性,因为"$pos[0]"
等向量分量表达式并非总被游戏识别。

下列代理(且仅限于这些键值)可识别向量分量:
- 所有代理的
resultVar
Clamp
: min, maxSine
: offset, max, min, periodLinearRamp
: rate, initial valueUniformNoise
: min, maxGaussianNoise
: min, max, mean, halfwidthWrapMinMax
: min, maxExponential
: 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
}
|
编写新代理
创建新代理较为简单。它们仅存在于客户端,应继承自IMaterialProxy
或其子类。
需要包含以下头文件:
"materialsystem/IMaterialProxy.h"
"materialsystem/IMaterialVar.h"
接口包含以下函数:
bool Init( IMaterial * pMaterial, KeyValues * pKeyValues )
- 材质首次预缓存 时调用。用于初始化变量并获取材质变量引用。成功返回true,失败返回false(代理将不运行)。
pKeyValues
包含来自VMT文件的代理参数。void OnBind( void* pC_BaseEntity )
- 材质即将在实体上渲染时调用。主要工作在此完成。
- 编码时需注意:所有使用该材质的实体共享同一材质对象,若修改某实体的材质参数将影响所有实体。由于
OnBind()
在每次实体渲染前调用,只要每次重新赋值所需值就不会出现问题。不要因无变化而提前返回,也不要在代理中存储输入数据。注意:
pC_BaseEntity
并不指向C_BaseEntity ,而是关联的IClientRenderable 。直接访问实体的最简单方法是继承自CEntityMaterialProxy
(位于proxyentity.h
),使用其提供的OnBind(C_BaseEntity*)
重载。 void Release()
- 待完善: 代理被移除时调用,但具体时机是?
IMaterial * GetMaterial()
- 代理所属的材质。
提示:若已存储材质变量,可返回
IMaterialVar::GetOwningMaterial()
而无需新建IMaterial
指针
接口
代理必须通过EXPOSE_INTERFACE
宏向材质暴露接口:
EXPOSE_INTERFACE( <className>, <interfaceName>, "<proxyName>" IMATERIAL_PROXY_INTERFACE_VERSION );
代理名称与接口版本间无逗号为刻意设计。
工具录制
橙盒版本中所有代理都添加了以下代码:
#include "toolframework_client.h"
void OnBind(...)
{
//...
if ( ToolsEnabled() )
ToolFramework_RecordMaterialParams( GetMaterial() );
}
这可能与Source Filmmaker 相关。建议在代理中添加此代码以兼容未来可能发布的电影制作工具!

CEntityMaterialProxy
会自动执行此调用已知问题



EntityRandom
)可能导致游戏崩溃 [todo tested in ?]另见
外部链接
- 使用代理的动态材质 - NoDraw.net关于材质代理实际应用的文章