MCJE着色器教程:从开发入门到游戏崩溃(三)—— 着色器程序JSON
前面的内容中,我们已经学习到了着色器的基本概念以及GLSL的基础语法,接下来,是时候将着色器运用到Minecraft中去了!或许你还没有完全掌握之前的内容,不过没有关系,本文中我们将会在讲解之中继续强化之前的内容。
在资源包中,minecraft命名空间下的shaders文件夹包含了着色器需要的文件,上一节中我们讲到的着色器将会被放在program文件夹中。除此之外,我们还需要一些json文件告诉我们资源包将如何调用我们的着色器。
作为参考和学习的手段,你可以解压version.jar以获得原版的资源包参照学习。
着色器程序json储存在program文件夹中。它是着色器和Minecraft之间的第一个接口。
格式如下:
blend
blend的有关设置相对生涩难懂一些,不过幸运的是,可能是因为minecraft的一些bug,对blend的修改似乎并不能起到对应的效果。当然,也有极大的可能是个人能力不足以解开此次blend的真正作用方式。因此一般情况下你只需要写一个固定的格式(见本小节末)即可。但是,如果能力足够,我仍然建议你学习这个小节。
下面的内容说明了blend本来应该起到的作用。
blend列表中的键值对决定了这个着色器将怎样将片段着色器和颜色缓冲区中的颜色混合叠加在一起。
func参数一共有六个可选的值,定义了混合时进行的基本的数学运算。我们说过,颜色一共有四个参数,即RBGA,分别表示红色,蓝色,绿色和透明度。四个分量将会被分别计算,下面的表格说明了func参数是如何影响这些计算的(frg表示片段着色器(源)的数据,af表示源的计算因子,old表示颜色缓冲区(目标)的数据,ao表示目标的计算因子,new表示结果;计算因子由blend中的其他几个参数决定,下文有说):
模式(func参数) | 运算式 | 描述 |
---|---|---|
add | new = old*ao + frg*af | 源颜色和目标颜色相互添加 |
subtract | new = old*ao – frg*af | 从源中减去目标 |
reversesubtract或reverse_ subtract | new = frg*af – old*ao | 从目标减去源 |
min | new = min(frg, old) | 输出颜色是源颜色和目标的最小值 |
max | new = max(frg, old) | 输出颜色是源颜色和目标的最大值 |
srcrgb、dstrgb、srcalpha、dstalpha四个参数都用于Minecraft源代码中的glBlendFunc或者glBlendFuncSeparate两个函数。这两个函数共同的作用就是在进行上文中func参数指定的计算前对目标颜色和源颜色进行预先的处理运算,例如增大,减小等等。不同的则是,glBlendFunc设置的运算方式是对于rgba一起作用的,而glBlendFuncSeparate则是对rgb和alpha分开作用的。若指指定前两个参数,则使用glBlendFunc,若四个参数均指定,则使用glBlendFuncSeparate。这很容易理解。根据名字可以很容易看出,scr开头的参数指源,dst开头的参数指目标,而rgb代表颜色,alpha代表透明度。这四个参数都只能为0
、1
、srccolor
、1-srccolor
、dstcolor
、1-dstcolor
、srcalpha
、1-srcalpha
、dstalpha
或1-dstalpha
中的一个。不区分大小写。忽略“_”。“1”、“0”、“-”可替换为“one”、“zero”、“minus”。接下来,我们用S表示源,D表示目标,算式的结果表示输出。注意,在这里,S和D都是[0, 1]之间的数字。
参数 | 算式 |
---|---|
| 0 |
| 1 |
| S |
| 1-S |
| D |
| 1-D |
| S |
1-srcalpha | 1-S |
| D |
| 1-S |
推荐直接写为:
"blend": { "func": "add", "srcrgb": "one", "dstrgb": "zero" }
参考资料
混合 – LearnOpenGL CN (learnopengl-cn.github.io)
现在,我用C++的OpenGL写了一个小程序,举一些简单的例子,用来更清晰地说明blend的作用。至少在我自己的C++程序中,blend的作用是正常的。
在第一个例子中,我们将在一个黑色窗口上渲染一个长方形。我们将它的RGBA设置为(0.6f, 0.8f, 0.3f, 0.7f)
,同时,在渲染前向glBlendFunc
函数传递GL_SRC_COLOR变量和GL_DST_COLOR变量(相当于Minecraft中的
和srccolor
)。那么我们根据理论知识进行预测,默认的混合方式是相加,黑色窗口的RGBA除了Alpha值均为0,因此乘以自身的RGB值以后仍然是0,长方形的RGBA值则是(0.6*0.6, 0.8*0.8, 0.3*0.3) ,那么最后渲染出的颜色即是 (0.36, 0.64, 0.09) 。dstcolor
下面是程序的执行结果,以及利用工具对屏幕进行颜色取样对输出的颜色进行检测
换算以后,rgb的值和我们的理论值是基本一致的,误差主要来源于取整。
接下来,我们再举一个例子。现在,我们将在一个窗口上渲染两个相互重叠的长方形,它们有着不同的颜色,并且各自有着不一样的alpha值。我们将一个长方形的RGBA值设置为(0.6f, 0.8f, 0.3f, 0.7f)
,另一个设置为(0.3f, 0.6f, 0.9f, 0.3f)
。同时,我们将glBlendFunc
函数的参数设置为GL_SRC_ALPHA和GL_ONE_MINUS_SRC_ALPHA(相当于Minecraft中的
和srcalpha
)。第一个长方形先于第二个长方形渲染。1-srcalpha
同样,我们进行预测。第一个长方形渲染的时候,自己的颜色将会作为源,而背景的黑色将会成为目标。因此第一个长方形渲染的时候,会将自己的RGB值乘以源的Alpha值,即0.7。第二个长方形渲染时,将分成两个部分。首先,和第一个长方形没有重叠的区域,第二个长方形的渲染方式和第一个长方形一样,以黑色的背景作为目标,自己的颜色作为源。而渲染和第一个长方形重叠的区域时,第一个长方形渲染的颜色将作为第二个长方形渲染的目标,而第二个长方形自己则会作为源。那么,我们的颜色计算就变成了这个样子,首先第二个长方形的RGB值乘以0.3,即自己(源)的Alpha值,然后加上第一个长方形渲染出的颜色乘以0.7(1-srcalpha
)的和作为最后渲染出的rgb值。我们这里列出红色通道,即RGB值的第一个数字的计算过程: (0.3*0.3) + (0.6*0.7)*0.7=97.92,四舍五入为98。其余两个颜色的计算,以及前面两个长方形未重叠部分颜色的计算,就交给读者自己计算了。三个颜色的具体的值都给在了下方以供参考。
cull
可选。布尔值,是否禁用多边形正面或者背面上的光照、阴影和颜色计算及操作,消除不必要的渲染计算,默认为true。
vertex和fragment
分别为顶点着色器和片段着色器的文件名(不包含文件拓展名)。着色器放置于与json同一级文件夹下,顶点着色器拓展名应为.vsh
,片段着色器为.fsh
。
attributes
硬编码,只能为["position"]
。
samplers
可以在着色器中使用的采样器列表。采样器将会在后文讲解post json的时候说明。你可以理解为一个画面或者图片,将会作为uniform变量(下面会讲)传入着色器。
uniforms
一个可以在着色器中使用的uniform变量列表。uniform变量是GLSL中变量的一种,和in、out变量同属于全局变量。在这个json文件中声明的uniform变量能在着色器程序中被直接调用。uniform的格式如下:
(注:还有一个name字符串用于声明uniform变量的名字,这里截图没有截进去)
对于某些特殊的name值,在Minecraft着色器中能代表特殊的意思。
下面是一个完整的着色器JSON实例:
{ "blend": { "func": "add", "srcrgb": "one", "dstrgb": "zero" }, "vertex": "sobel", "fragment":"prog1", "attributes":["Position"], "samplers":[ {"name":"DiffuseSampler"}, {"name":"test"} ], "uniforms":[ { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, { "name": "InSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ] }, { "name": "OutSize", "type": "float", "count": 2, "values": [ 1.0, 1.0 ] } ] }
注意,其中uniforms中的几个变量是必须写的,否则会导致游戏崩溃。