Zh/Material proxies: Difference between revisions

From Valve Developer Community
< Zh
Jump to navigation Jump to search
m (obsolete language category)
No edit summary
Line 1: Line 1:
{{lang|Material proxies}}
{{LanguageBar}}
{{toc-right}}
{{translate}}
{{translate}}
Translated by ChatGPT


借助'''材质代理''',游戏的已编译的C++代码得以控制{{L|material|材质}}的属性。不少材质代理只用于执行特定的任务, 但是 {{L|List Of Material Proxies|这里也存在一些通用的材质代理}},它们在VMT文件内提供一些最基本的脚本编程支持。
{{toc-right}}
'''材质代理'''允许游戏的编译C++代码操作[[材质]]的属性。许多代理执行特定任务,但[[材质代理列表|也有其他更通用的代理]],它们共同在VMT文件中提供基本的脚本支持。{{clarify}}


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


{{bug|一些用户说工具模式下的游戏不会启用一些功能性的材质代理。}}
{{bug|一些用户报告说工具模式下某些功能代理在游戏中无法运行。}}
{{todo|bug的程度未经测试。}}
{{todo|该bug的范围尚未测试。}}


{{bug| 在诸如{{L|env_smokestack}}等动态生成的粒子中使用某些材质代理,会导致游戏在地图加载阶段崩溃。}}
{{bug|使用某些材质代理在动态创建的粒子上时,例如[[env_smokestack]],会导致游戏在加载地图时崩溃。}}


== 用法 ==
== 用法 ==


这是一个<code>Sine(正弦)</code>材质代理,提供在8秒内对材质进行{{L|$alpha|淡入淡出}}操作的功能:
该材质有一个 <code>Sine</code> 代理,使其在8秒内逐渐[[$alpha|显现和消失]]:


<source lang=php>
<source lang=php>
Line 21: Line 22:
$basetexture shadertest/LightmappedTexture
$basetexture shadertest/LightmappedTexture


Proxies // 代理的代码块
Proxies // 代理列在此块内
{
{
Sine // 产生正弦波的代理
Sine // 产生正弦波的代理
{
{
resultVar $alpha // 需要被控制的着色器参数
resultVar $alpha // 要操作的着色器参数
sineperiod 8
sineperiod 8
sinemin 0
sinemin 0
Line 36: Line 37:
=== 变量 ===
=== 变量 ===


材质可以在内部使用中声明它们的自有变量。这样的变量必须被声明于代理代码块之外(VMT的主体中),并且必须在右侧给定一个默认值。
材质可以声明自己的变量供内部使用。这些变量必须在Proxies块之外声明,在材质的主体部分,并且必须指定默认值。
 
这些自定义变量可能用于在代理之间传递结果或向其提交硬编码的数据。它们通常用于将数学函数代理(例如 <code>Add</code>、<code>Subtract</code> 等)串联成更长的方程式。对于2D/3D/4D向量(<code>"[0 0 0]"</code>),变量名称可以附加<code>[0]</code>以读取/写入特定索引。写入索引变量时应将其包围在引号中。


These custom variables might be used to pass results between proxies or to submit hard-coded data to them. They are often employed to chain mathematic function proxies (i.e. <code>Add</code>, <code>Subtract</code>, etc) together into longer equations. For 2D/3D vectors (<code>"[0 0 0]"</code>), the variable name can have <code>[0]</code> suffixed to read/write a specific index. Writes to indexed variables should be encased in quotes.
引擎使用8位有符号整数对这些自定义变量进行编码。因此,每个材质最多支持128个唯一自定义变量。


This example extends the one above by staggering the starting position of the sine wave:
以下示例通过错开正弦波的起始位置来扩展上述示例:


<source lang=php>
<source lang=php>
Line 47: Line 50:
$basetexture shadertest/LightmappedTexture
$basetexture shadertest/LightmappedTexture


$offset 0 // declare custom var
$offset 0 // 声明自定义变量
Proxies
Proxies
Line 53: Line 56:
EntityRandom
EntityRandom
{
{
resultVar $offset // write to custom var
resultVar $offset // 写入自定义变量
}
}
Sine
Sine
{
{
resultVar $alpha
resultVar $alpha
timeoffset $offset // read from custom var
timeoffset $offset // 从自定义变量读取
sineperiod 8
sineperiod 8
sinemin 0
sinemin 0
Line 67: Line 70:
</source>
</source>


Now each entity this material is used on pulses to its own schedule.
现在,每个使用该材质的实体都会按照自己的时间表脉冲。


Other examples for writing to indexed variables:
写入索引变量的其他示例:


<source lang=php>
<source lang=php>
Line 103: Line 106:
</source>
</source>


An example of using a color vector to create 'random' color pulses.
使用颜色向量创建“随机”颜色脉冲的示例:


<source lang=php>
<source lang=php>
Line 169: Line 172:
</source>
</source>


An example of a dynamic texture transform.
动态纹理变换的示例。


== Splitting a vector ==
== 分割向量 ==
Using vectors is quirky. Not all proxies recognize vector components. If a vector's components need be processed separately, they need to be split into different variables first.
使用向量时有些特殊之处。并非所有代理都能识别向量分量。如果需要单独处理向量的各个分量,则必须先将它们拆分成不同的变量。 
所有代理中的<code>resultVar</code>都会识别向量分量。


The following proxies, and only these keyvalues, can recognize vector components:
以下代理和仅这些关键字可以识别向量分量: 
<code>
<code>
* '''Clamp: '''        min, max
* '''Clamp: '''        min, max
* '''Sine:'''          offset, max, min, period
* '''Sine:'''          offset, max, min, period
* '''LinearRamp:'''    rate, initial value
* '''LinearRamp:'''    rate, initial value
* '''UniformNoise:'''  min, max
* '''UniformNoise:'''  min, max
* '''GaussianNoise:''' min, max, mean, halfwidth
* '''GaussianNoise:''' min, max, mean, halfwidth
* '''WrapMinMax:'''    min, max
* '''WrapMinMax:'''    min, max
* '''Exponential:'''  min, max, scale, offset
* '''Exponential:'''  min, max, scale, offset
</code>
</code>
All of the above can be used to split a vector. However Clamp is the {{L|Cheap|cheapest}} to use:
上述所有代理都可以用于分割向量。 但是Clamp是[[Cheap|最便宜]]的选择: 
 
<source lang="php">
<source lang="php">
         $pos "[0 0 0]"
         $pos "[0 0 0]"
         $posX .0        //must be float or Clamp will not save the value properly
         $posX .0        //必须为浮点数,否则Clamp无法正确保存值
         $posY .0        //must be float or Clamp will not save the value properly
         $posY .0        //必须为浮点数,否则Clamp无法正确保存值
         $posZ .0        //must be float or Clamp will not save the value properly
         $posZ .0        //必须为浮点数,否则Clamp无法正确保存值
          
          
         $zero 0
         $zero 0
          
          
         //Proxy that outputs a 3d vector
         //输出3D向量的代理
         PlayerPosition
         PlayerPosition
         {
         {
Line 200: Line 205:
         }
         }
          
          
         //Split the 3d vector for further use
         //拆分3D向量以便进一步使用
         Clamp
         Clamp
         {
         {
Line 226: Line 231:
</source>
</source>


{{warning|Quotes are needed when addressing a vector's component, or when defining a vector.}}
{{warning|引用时需要加引号,以便正确处理向量分量或定义向量。}}


== Writing new proxies ==
== 编写新的代理 ==


New proxies are easy to create. They exist on the client only and should inherit from <code>IMaterialProxy</code> or one of its descendants.
创建新的代理很简单。它们仅存在于客户端,并应继承自<code>IMaterialProxy</code>或其子类。


You will need these #includes:
你需要这些头文件:


* <code>"materialsystem/IMaterialProxy.h"</code>
* <code>"materialsystem/IMaterialProxy.h"</code>
* <code>"materialsystem/IMaterialVar.h"</code>
* <code>"materialsystem/IMaterialVar.h"</code>


These functions are included in the interface:
以下函数包含在接口中:
 
; <code>[[bool]] Init( [[IMaterial]]* pMaterial, [[KeyValues]]* pKeyValues )</code> 
: 在材质首次[[预加载]]时调用。使用此函数初始化变量并获取你将使用的材质变量的引用。成功时返回true,失败时返回false(此时代理将不会执行)。 
: <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>{{L|bool}} Init( {{L|IMaterial}}* pMaterial, {{L|KeyValues}}* pKeyValues )</code>
; <code>[[IMaterial]]* GetMaterial()</code>
: Called when the material is first {{L|precache}}d. Use this function to initialise variables and grab references to the material vars you will be using. Return true on success and false on failure (in which case the proxy will not be run).
: 获取代理所附加的材质。{{tip|如果你有一个材质变量存储,你可以返回<code>IMaterialVar::GetOwningMaterial()</code>,而不是创建一个新的<code>IMaterial</code>指针。}}
: <code>pKeyValues</code> contains the proxy parameters from the VMT file.
; <code>void OnBind( void* pC_BaseEntity )</code>
: Called when the material is about to be rendered on an entity. This is where the work is done.
: When coding this function it is important to remember that all entities using a material share the same material object, and that if you change it on one entity it changes everywhere else too. Since <code>OnBind()</code> is called every time an entity comes up for rendering this is not a problem ''so long as you reassign the value you want every time''. Don't return early just because there has been no change, and don't store any input data in the proxy. {{note|<code>pC_BaseEntity</code> doesn't lead to a <code>{{L|C_BaseEntity}}</code> as its name suggests, but rather to the associated <code>{{L|IClientRenderable}}</code>. The easiest way to access the entity directly is to base your class on <code>CEntityMaterialProxy</code> (in <code>proxyentity.h</code>) and use the <code>OnBind(C_BaseEntity*)</code> overload it provides.}}
; <code>void Release()</code>
: {{todo|Called when the proxy is removed, but when is that?}}
; <code>{{L|IMaterial}}* GetMaterial()</code>
: The material the proxy is attached to. {{tip|If you have a material var stored, you can return <code>IMaterialVar::GetOwningMaterial()</code> here instead of creating a new <code>IMaterial</code> pointer.}}


=== Interface ===
=== 接口 ===


The proxy must expose its interface to materials with the <code>EXPOSE_INTERFACE</code> macro:
代理必须通过<code>EXPOSE_INTERFACE</code>宏向材质暴露其接口:


<source lang=cpp>
<source lang=cpp>
Line 258: Line 266:
</source>
</source>


The lack of a comma between the proxy name and interface version is intentional.
代理名称和接口版本之间没有逗号,这是故意的。


=== Tools recording ===
=== 工具录制 ===


This code was added to all proxies in the Orange Box:
以下代码已被添加到所有代理中,适用于Orange Box:


<source lang=cpp>
<source lang=cpp>
Line 276: Line 284:
</source>
</source>


It's probably related to the {{L|Source Filmmaker}}. It's a good idea to add it to your proxy too in case the Filmmaker is ever released!
它可能与[[Source Filmmaker]]相关。最好在你的代理中也添加这段代码,以防Filmmaker发布!
 
{{tip|<code>CEntityMaterialProxy</code> makes the call by itself.}}


== See also ==
{{tip|<code>CEntityMaterialProxy</code>会自动进行此调用。}}
* {{L|List Of Material Proxies}}
* {{L|Material Creation}}


== External links ==
== 另见 ==
* [https://web.archive.org/web/20140719034333/http://www.nodraw.net/2010/01/dynamic-materials-with-proxies/ Dynamic Materials With Proxies] - An article covering practical uses of materials with proxies by [https://www.nodraw.net NoDraw.net]
* [[材质代理列表]]
* [[材质代理编程]]
* [[材质创建]]
* Steam指南,解释更多关于各种代理的内容。 [https://steamcommunity.com/sharedfiles/filedetails/?id=594255575 基础], [https://steamcommunity.com/sharedfiles/filedetails/?id=668958242 RNG], [https://steamcommunity.com/sharedfiles/filedetails/?id=749130424 "编程"]


{{ACategory|Material System}}
== 外部链接 ==
{{ACategory|Programming}}
* [https://nodraw.net/2010/01/dynamic-materials-with-proxies/ 使用代理的动态材质] - [http://www.nodraw.net NoDraw.net]关于使用代理的材质的实践文章

Revision as of 04:42, 23 December 2024

English (en)Português do Brasil (pt-br)中文 (zh)Translate (Translate)
Info content.png
This page is Machine translated
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.(en)
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.


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 complies with the alternate languages guide.(en)

Translated by 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会自动进行此调用。

另见

外部链接