Zh/Material proxies: Difference between revisions
No edit summary |
m (deepseek translation) |
||
Line 1: | Line 1: | ||
{{LanguageBar}} | {{LanguageBar}} | ||
{{translate}} | {{translate}} | ||
{{toc-right}} | {{toc-right}} | ||
'''材质代理''' | '''材质代理'''允许游戏的编译后C++代码在运行时操作[[material|材质]]属性,可用于创建动态或动画纹理。 | ||
{{bug| | {{bug|有用户报告称工具模式无法在游戏中运行某些功能代理。此问题的具体影响范围未经测试。}} | ||
{{ | {{bug|在动态创建的粒子(例如{{L|env_smokestack}})上使用某些材质代理会导致地图加载时游戏崩溃。}} | ||
== 使用方法 == | |||
材质代理可通过[[VMT]]文件的<code>Proxies</code>[[KeyValues]]块添加到材质中。该块内的每个条目即为一个'''代理'''。一个材质可以添加任意数量的代理,代理按出现顺序执行。游戏会以极快的间隔反复执行材质代理列表。{{inline note|name=每帧执行?}} | |||
代理名称决定了其参数和功能。 | |||
许多代理执行特定任务,但也有一些通用代理可为VMT文件提供基础脚本支持。{{clarify}} | |||
{{main|List of material proxies}} | |||
通常每个代理都有一个输出值(例如[[float|浮点数]]),该值会被写入其<code>resultVar</code>(可以是着色器参数或变量,参见[[#Variables|变量]]章节)。 | |||
例如以下材质使用{{material proxy|Sine}}代理生成浮点数,并写入材质的{{ent|$alpha}}着色器参数,使纹理以8秒周期渐隐渐现: | |||
<source lang=php> | <source lang=php> | ||
LightmappedGeneric | LightmappedGeneric | ||
Line 22: | Line 22: | ||
$basetexture shadertest/LightmappedTexture | $basetexture shadertest/LightmappedTexture | ||
Proxies // | Proxies // 代理列表在此块中定义 | ||
{ | { | ||
Sine // | Sine // 生成正弦波值的代理 | ||
{ | { | ||
sineperiod 8 // 正弦周期长度(秒) | |||
sinemin 0 // 最小输出值 | |||
sinemin 0 | sinemax 1 // 最大输出值 | ||
sinemax 1 | resultVar $alpha // 振荡输出值的写入目标 | ||
} | } | ||
} | } | ||
Line 37: | Line 37: | ||
=== 变量 === | === 变量 === | ||
材质可以声明自定义变量供内部使用。此类变量必须在<code>Proxies</code>块之外的材质主体中声明,且必须指定右侧的默认值。 | |||
这些自定义变量可用于在代理间传递结果或向其提交硬编码数据。常用于将数学函数代理(如{{material proxy|Add|加法}}、{{material proxy|Subtract|减法}}等)链接成复杂公式。对于2D/3D/4D向量(<code>$vec "[0 0 0]"</code>),变量名可添加<code>[0]</code>后缀(<code>"$vec[0]"</code>)来读写特定索引。对索引变量的写入操作需用引号包裹。 | |||
引擎使用8位有符号整数编码这些自定义变量,因此每个材质最多有128个唯一自定义变量。 | |||
此示例扩展了前例,使用{{material proxy|EntityRandom}}代理生成随机值来错开正弦波的起始位置: | |||
<source lang=php> | <source lang=php> | ||
LightmappedGeneric | LightmappedGeneric | ||
Line 50: | Line 49: | ||
$basetexture shadertest/LightmappedTexture | $basetexture shadertest/LightmappedTexture | ||
$offset 0 // | $offset 0 // 声明自定义变量($offset并非游戏识别的着色器参数) | ||
Proxies | Proxies | ||
{ | { | ||
EntityRandom | EntityRandom // 生成随机数 | ||
{ | { | ||
resultVar $offset // 写入自定义变量 | resultVar $offset // 写入自定义变量 | ||
Line 70: | Line 69: | ||
</source> | </source> | ||
现在使用该材质的每个实体都会按独立节奏脉动。 | |||
以下是通过写入索引变量使用$color向量创建"随机"颜色脉动的示例: | |||
<source lang=php> | <source lang=php> | ||
$color "[0 0 0]" | $color "[0 0 0]" // 非自定义变量,游戏识别"$color" | ||
proxies | proxies | ||
Line 106: | Line 104: | ||
</source> | </source> | ||
{{expand|noborder=1|title=动态纹理变换示例| | |||
<source lang=php> | <source lang=php> | ||
UnlitGeneric | UnlitGeneric | ||
Line 171: | Line 169: | ||
} | } | ||
</source> | </source> | ||
}} | |||
=== 向量拆分 === | |||
使用向量较为特殊,因为形如<code>"$pos[0]"</code>的向量分量表达式有时不被游戏识别。 | |||
== | {{important|在寻址向量分量或定义向量时必须使用引号}} | ||
以下代理及其键值可以识别向量分量: | |||
<tt> | |||
* 所有代理的<code>resultVar</code> | |||
* {{material proxy|Clamp}}: min, max | |||
<code> | * {{material proxy|Sine}}: offset, max, min, period | ||
* | * {{material proxy|LinearRamp}}: rate, initial value | ||
* | * {{material proxy|UniformNoise}}: min, max | ||
* | * {{material proxy|GaussianNoise}}: min, max, mean, halfwidth | ||
* | * {{material proxy|WrapMinMax}}: min, max | ||
* | * {{material proxy|Exponential}}: min, max, scale, offset | ||
* | </tt> | ||
* | 如需单独处理向量分量,需先将其拆分到不同变量中。 | ||
</ | 所有上述代理均可用于拆分向量,但{{material proxy|Clamp}}性能消耗最低。 | ||
{{expand|noborder=1|title=示例| | |||
<source lang="php"> | <source lang="php"> | ||
$pos "[0 0 0]" | $pos "[0 0 0]" | ||
$posX .0 // | $posX .0 //必须是浮点数,否则Clamp无法正确保存值 | ||
$posY .0 // | $posY .0 //必须是浮点数,否则Clamp无法正确保存值 | ||
$posZ .0 // | $posZ .0 //必须是浮点数,否则Clamp无法正确保存值 | ||
$zero 0 | $zero 0 | ||
Line 205: | Line 203: | ||
} | } | ||
// | //拆分3D向量供后续使用 | ||
Clamp | Clamp | ||
{ | { | ||
Line 230: | Line 228: | ||
} | } | ||
</source> | </source> | ||
}} | |||
== 编写新代理 == | |||
创建新代理较为简单。代理仅存在于客户端,应继承自<code>IMaterialProxy</code>或其子类。 | |||
需要包含以下头文件: | |||
* <code>"materialsystem/IMaterialProxy.h"</code> | |||
* <code>"materialsystem/IMaterialProxy.h"</code> | |||
* <code>"materialsystem/IMaterialVar.h"</code> | * <code>"materialsystem/IMaterialVar.h"</code> | ||
接口包含以下函数: | |||
; <code>[[bool]] Init( [[IMaterial]]* pMaterial, [[KeyValues]]* pKeyValues )</code> | ; <code>[[bool]] Init( [[IMaterial]]* pMaterial, [[KeyValues]]* pKeyValues )</code> | ||
: | : 材质首次[[precache|预缓存]]时调用。用于初始化变量并获取材质变量引用。成功返回true,失败返回false(此时代理不会运行)。 | ||
: <code>pKeyValues</code>包含来自VMT文件的代理参数。 | : <code>pKeyValues</code>包含来自VMT文件的代理参数。 | ||
; <code>void OnBind( void* pC_BaseEntity )</code> | |||
: 材质即将在实体上渲染时调用。在此执行主要逻辑。 | |||
: 需注意所有使用该材质的实体共享同一个材质对象。若在某实体上修改材质参数,将影响所有使用该材质的实体。由于<code>OnBind()</code>在每次实体渲染时都会调用,''只要每次重新赋值所需值''就不会出现问题。不要因数据未变化而提前返回,也不要在代理中存储输入数据。{{note|<code>pC_BaseEntity</code>并不指向<code>[[C_BaseEntity]]</code>,而是关联的<code>[[IClientRenderable]]</code>。继承自<code>CEntityMaterialProxy</code>(位于<code>proxyentity.h</code>)并使用其提供的<code>OnBind(C_BaseEntity*)</code>重载可便捷访问实体。}} | |||
; <code>void Release()</code> | |||
: {{todo|代理被移除时调用,但具体触发时机是?}} | |||
; <code>[[IMaterial]]* GetMaterial()</code> | |||
: 获取代理绑定的材质。{{tip|若已存储材质变量,可直接返回<code>IMaterialVar::GetOwningMaterial()</code>,无需新建<code>IMaterial</code>指针。}} | |||
=== 接口暴露 === | |||
=== | |||
代理需使用<code>EXPOSE_INTERFACE</code>宏向材质系统暴露接口: | |||
<source lang=cpp> | <source lang=cpp> | ||
Line 266: | Line 260: | ||
</source> | </source> | ||
代理名称与接口版本间无逗号为设计特性。 | |||
=== 工具录制 === | === 工具录制 === | ||
橙盒版本中所有代理都添加了以下代码: | |||
<source lang=cpp> | <source lang=cpp> | ||
Line 284: | Line 278: | ||
</source> | </source> | ||
此代码可能与{{L|Source Filmmaker}}相关。建议在自定义代理中添加此代码以兼容未来可能的电影工坊发布! | |||
{{tip|<code>CEntityMaterialProxy</code> | {{tip|<code>CEntityMaterialProxy</code>已自动实现该调用。}} | ||
== 另见 == | == 另见 == | ||
* | * {{L|List Of Material Proxies|材质代理列表}} | ||
* | * {{L|Material proxies programming|材质代理编程}} | ||
* | * {{L|Material Creation|材质创建}} | ||
* | * Steam指南详解各类代理:[https://steamcommunity.com/sharedfiles/filedetails/?id=594255575 基础]、[https://steamcommunity.com/sharedfiles/filedetails/?id=668958242 随机数生成]、[https://steamcommunity.com/sharedfiles/filedetails/?id=749130424 "编程"] | ||
== 外部链接 == | == 外部链接 == | ||
* [https://nodraw.net/2010/01/dynamic-materials-with-proxies/ | * [https://nodraw.net/2010/01/dynamic-materials-with-proxies/ 使用代理实现动态材质] - [http://www.nodraw.net NoDraw.net]的实战应用指南 | ||
{{ACategory|Source}} | |||
{{ACategory|Material System}} | |||
{{ACategory|C++}} |
Revision as of 18:45, 27 January 2025

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.

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 complies with the alternate languages guide.
材质代理允许游戏的编译后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
已自动实现该调用。另见
外部链接
- 使用代理实现动态材质 - NoDraw.net的实战应用指南