feat:上传mcguide-开发指南部份
146
mcguide/16-美术/7-材质与着色器/1-材质介绍与着色器入门.md
Normal file
@@ -0,0 +1,146 @@
|
||||
---
|
||||
front: https://nie.res.netease.com/r/pic/20220408/3541655a-7205-4609-b118-be8d6dbb84bd.png
|
||||
hard: 入门
|
||||
time: 15分钟
|
||||
selection: 38
|
||||
---
|
||||
|
||||
# 材质与着色器介绍
|
||||
|
||||
## 前言
|
||||
|
||||
本文将介绍游戏材质和着色器的基本概念,以及MC游戏中使用的着色器的基本编写与使用方式
|
||||
|
||||
|
||||
## 材质和着色器
|
||||
|
||||
``材质``(material)定义了游戏物体的画面表现,用来描述一个游戏内的物体应该如何被渲染,其中的信息包括物体使用的纹理、**着色器**、采样方式、是否进行透明混合以及面片的剔除模式等。
|
||||
``着色器``(shader)是一种依附于材质的渲染程序,用于具体实现材质在渲染时的某些逻辑,一般包括动画、着色、发光以及半透明等效果。
|
||||
例如,一个砖块的材质一般需要定义一个不透明材质,规定这个材质仅使用一张纹理(一般是砖块贴图),并指定一个着色器用来渲染这个砖块。砖块着色器会根据砖块的模型数据,结合砖块贴图、砖块位置以及场景光照等信息,计算出砖块在屏幕上对应像素的颜色。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
## 在MC中定义材质
|
||||
|
||||
MC使用json来定义材质信息,这些信息包括使用哪些shader、启用哪些宏定义、启用哪些片元测试等。不同类型的材质会分开存放在不同的材质文件中,例如地形相关的材质会放在terrain.material文件中,实体、骨骼模型相关的材质会放在entity.material文件中,具体的对应关系可以参考[内置材质清单](./2-内置材质清单.md),具体的材质配置格式可以参考[材质配置说明](./3-材质配置说明.md)或者参照具体的.material文件。
|
||||
|
||||
## OpenGL与GLSL
|
||||
|
||||
目前MC的渲染(非渲染龙)是基于OpenGL实现的,不同平台的不同设备支持的OpenGL版本各有不同,越高级的OpenGL版本能使用的特性就越多,但因此兼容的设备就越少,开发者应考虑shader所使用的特性是否能够兼容目标设备。
|
||||
|
||||
MC中的shader使用glsl进行编写,这是一种较为简单的类C语言,对于C或者C族系语言有所熟悉的开发者可以很快掌握。具体的glsl语法可以在参考[图形管线与GLSL简单入门](./6-图形管线与GLSL简单入门.md)。
|
||||
|
||||
## MC材质SDK接口
|
||||
|
||||
我的世界中国版允许使用python接口对材质产生运行时影响,涉及模块包括`后处理`、`网易骨骼模型`以及`原版模型`等,具体的使用方式我们将在后面的文档中进行举例介绍,更详细的说明可以在API文档页面中进行查询。后续我们会开发并开放更多有关材质的SDK接口给开发者使用。
|
||||
|
||||
## 材质中的宏
|
||||
|
||||
开发者可以自定义shader宏,可以通过在材质定义的`defines`字段中进行添加:
|
||||
```json
|
||||
"terrain_blend_far:terrain_blend": {
|
||||
"+defines": [ "FOG" ]
|
||||
}
|
||||
```
|
||||
这里为`terrain_blend_far`材质增加了宏定义`FOG`,类似于C语言,我们可以在shader中使用`ifdef`或者`ifndef`针对`FOG`进行宏判断并做一些特殊逻辑处理:
|
||||
```opengl
|
||||
#ifndef FOG
|
||||
color.rgb += FOG_COLOR.rgb * 0.000001;
|
||||
#endif
|
||||
```
|
||||
|
||||
## 头文件
|
||||
|
||||
MC中的shader允许使用头文件,只要将头文件拓展名改为`.h`并放在`shaders/glsl`目录下,shader代码就可以引用这个头文件:
|
||||
```opengl
|
||||
#include "uniformWorldConstants.h"
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Uniform
|
||||
|
||||
在shader中除了使用顶点属性以外,引擎还会通过uniform变量向shader中传递很多有用的数据,大部份shader都依赖一些uniform变量进行计算。同时,开发者可以利用<a href="../../../mcdocs/1-ModAPI/接口/模型.html#setextrauniformvalue" rel="noopenner">SetExtraUniformValue接口</a>设置我们预留的Uniform变量值来实现效果。引擎内置的uniform变量定义在各个.h头文件中,下面将对常用的头文件及其中的uniform变量进行说明。
|
||||
|
||||
### uniformWorldConstants.h
|
||||
|
||||
生效时机:在渲染场景对象的shader中可以使用,里面包含当前场景相机对应的变换矩阵
|
||||
```opengl
|
||||
MAT4 WORLDVIEWPROJ : mvp矩阵乘积,用于把模型空间的坐标转化为裁剪空间坐标
|
||||
MAT4 WORLD : 世界矩阵,用于把模型空间的坐标转化为世界坐标
|
||||
MAT4 WORLDVIEW : mv矩阵乘积,用于把模型空间的坐标转化为视口空间的坐标
|
||||
MAT4 PROJ : 投影矩阵,用于把视口空间的坐标转化为裁剪空间坐标
|
||||
```
|
||||
|
||||
### uniformPerFrameConstants.h
|
||||
|
||||
生效时机:每帧更新,所有shader中都可以使用
|
||||
```opengl
|
||||
vec3 VIEW_POS : 相机位置
|
||||
float TIME : 游戏启动到现在经过的时间,可以用于做一些动画,为了避免无限增长带来较大浮点误差,会取模210,处理数值边界的时候需要注意
|
||||
vec4 FOG_COLOR : 雾的颜色
|
||||
vec2 FOG_CONTROL : 雾生效的距离,FOG_CONTROL.x为最短距离,FOG_CONTROL.y为最远距离
|
||||
float RENDER_DISTANCE : 可渲染的最远距离
|
||||
float FAR_CHUNKS_DISTANCE : 区块渲染的最远距离(水下和雾会影响区块渲染的最远距离)
|
||||
```
|
||||
|
||||
### uniformRenderChunkConstants.h
|
||||
|
||||
生效时机:渲染地形
|
||||
```opengl
|
||||
POS4 CHUNK_ORIGIN_AND_SCALE : 基于玩家视角的Chunk的局部位置
|
||||
POS4 CHUNK_WORLD_POS_MOD_VALUE : 基于世界坐标的Chunk的世界位置,但由于MC地图很大,所以这里数值会取模128
|
||||
float RENDER_CHUNK_FOG_ALPHA : Chunk雾效的透明度
|
||||
```
|
||||
|
||||
### uniformShaderConstants.h
|
||||
|
||||
生效时机:所有shader中都可以使用
|
||||
```opengl
|
||||
vec4 CURRENT_COLOR : 受群系等因素影响,场景中渲染的对象会对应的明亮两种颜色,这个为明亮的颜色。其它系统也可能复用此值作一些颜色传递
|
||||
vec4 DARKEN : 受群系等因素影响,场景中渲染的对象会对应的明亮两种颜色,这个为暗的颜色。其它系统也可能复用此值作一些颜色传递
|
||||
vec3 TEXTURE_DIMENSIONS :当前渲染使用的第一张贴图的尺寸,常为图集贴图,x,y,z分别为宽,高,当前所处在mipmap的哪一级。常用于做抗锯齿处理
|
||||
float HUD_OPACITY : 一些ui渲染的透明度会变化,使用此值进行控制
|
||||
```
|
||||
|
||||
### uniformWeatherConstants.h
|
||||
|
||||
生效时机:渲染天气元素的时候生效
|
||||
```opengl
|
||||
vec4 POSITION_OFFSET : 当前天气渲染用的面片的坐标偏移
|
||||
vec4 VELOCITY : 风速
|
||||
vec4 ALPHA : 存储当前的光照缩放值
|
||||
vec4 VIEW_POSITION : 相对于相机的位置
|
||||
vec4 SIZE_SCALE : 粒子大小的缩放值,粒子会根据投影,速度进行一定缩放
|
||||
vec4 FORWARD :当前视角的前向位置,一般用于把粒子往前放推一点,保证在相机前面
|
||||
vec4 UV_INFO : 渲染时候的贴图的uv
|
||||
vec4 PARTICLE_BOX : 播放粒子的区域大小
|
||||
```
|
||||
|
||||
### uniformEntityConstants.h
|
||||
|
||||
生效时机:渲染实体的时候生效
|
||||
```opengl
|
||||
vec4 OVERLAY_COLOR :覆盖该实体的颜色,例如生物被火烧、受到伤害或者苦力怕膨胀时的颜色。
|
||||
vec4 TILE_LIGHT_COLOR : xyz代表光照强度,w代表光照方向。
|
||||
vec4 GLINT_COLOR : 附魔颜色
|
||||
vec2 UV_OFFSET : 附魔纹理UV偏移
|
||||
vec2 UV_ROTATION : 附魔纹理UV旋转
|
||||
vec2 GLINT_UV_SCALE : 附魔纹理UV缩放值
|
||||
```
|
||||
|
||||
### uniformFrameConstants.h
|
||||
|
||||
生效时机:每帧更新,所有shader中都可以使用
|
||||
```opengl
|
||||
float TOTAL_REAL_WORLD_TIME :现实世界的时间流逝,单位为秒,范围为[0, 3600]。
|
||||
```
|
||||
|
||||
### util.h
|
||||
|
||||
封装了抗锯齿的采样函数texture2D_AA
|
||||
276
mcguide/16-美术/7-材质与着色器/2-内置材质清单.md
Normal file
@@ -0,0 +1,276 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 分钟
|
||||
---
|
||||
|
||||
# MC内置材质清单
|
||||
|
||||
## 前言
|
||||
|
||||
本文列出了MC游戏中常用的内置材质,若开发者需要修改使用某些材质,则可参考此文。
|
||||
|
||||
## 天空
|
||||
|
||||
### sky.material 材质文件
|
||||
|
||||
#### sun_moon
|
||||
|
||||
太阳和月亮
|
||||
|
||||
#### stars
|
||||
|
||||
星星
|
||||
|
||||
#### cubemap
|
||||
|
||||
用于渲染天空的六个面组成的天空盒
|
||||
|
||||
#### skyplane
|
||||
|
||||
用于渲染上方天空的颜色。游戏里面最上方会放置一个超大圆型网格,平行于地面
|
||||
|
||||
#### end_sky
|
||||
|
||||
用于渲染末地上方天空的颜色与贴图。游戏里面最上方会放置一个超大圆型网格,平行于地面
|
||||
|
||||
### fancy.material 、 sad.material 材质文件
|
||||
|
||||
#### clouds
|
||||
|
||||
云
|
||||
|
||||
## 地形方块(放置在地上的方块)
|
||||
|
||||
### terrain.material 材质文件
|
||||
|
||||
#### terrain_opaque
|
||||
|
||||
不透明地形方块
|
||||
|
||||
##### terrain_far
|
||||
|
||||
不透明地形方块在远处的时候会使用该材质进行渲染
|
||||
|
||||
#### terrain_blend
|
||||
|
||||
半透明地形方块材质,例如水,玻璃等
|
||||
|
||||
##### terrain_blend_far
|
||||
|
||||
半透明方块在远处的时候会使用该材质进行渲染
|
||||
|
||||
#### terrain_alpha
|
||||
|
||||
一些局部有全透明区域,而且正反面都需要渲染的方块材质,引擎中用到的有铁砧,竹子,铁轨,药水,仙人掌,珊瑚,农作物等等。
|
||||
|
||||
##### terrain_alpha_single_side
|
||||
|
||||
一些局部有全透明区域,但开启了背面裁剪只渲染一个面的材质,引擎中用到的有信标,胡萝卜,红石比较器,堆肥箱,门,蘑菇等等。
|
||||
|
||||
#### terrain_doubleside
|
||||
|
||||
目前用于渲染床
|
||||
|
||||
#### terrain_opaque_seasons
|
||||
|
||||
目前用于渲染上面覆盖有雪的不透明树叶
|
||||
|
||||
##### terrain_seasons_far
|
||||
|
||||
目前用于渲染远处上面覆盖有雪的不透明树叶
|
||||
|
||||
#### terrain_alpha_seasons
|
||||
|
||||
目前用于渲染上面覆盖有雪的半透明树叶
|
||||
|
||||
##### terrain_seasons_far_alpha
|
||||
|
||||
目前用于渲染渲染上面覆盖有雪的半透明树叶
|
||||
|
||||
### barrier.material 材质文件
|
||||
|
||||
#### barrier
|
||||
|
||||
屏障方块
|
||||
|
||||
### portal.material 材质文件
|
||||
|
||||
#### portal_base
|
||||
|
||||
末地传送门
|
||||
|
||||
## 非地形方块与实体(手持或者独立在场景中的)
|
||||
|
||||
### entity.material 材质文件
|
||||
|
||||
#### entity_static
|
||||
|
||||
静态实体
|
||||
|
||||
#### entity_flat_color_line
|
||||
|
||||
用于渲染钓鱼竿的线
|
||||
|
||||
#### entity_loyalty_rope
|
||||
|
||||
用于渲染拴绳
|
||||
|
||||
#### opaque_block 、 opaque_block_color 、 opaque_block_color_uv2
|
||||
|
||||
不透明方块渲染
|
||||
|
||||
#### alpha_block 、 alpha_block_color
|
||||
|
||||
带透明区域的方块
|
||||
|
||||
#### map
|
||||
|
||||
地图渲染
|
||||
|
||||
#### entity_alphablend 、 entity_alphablend_nocolor
|
||||
|
||||
带透明度混合的实体对象
|
||||
|
||||
#### item_in_hand 、 item_in_hand_multicolor_tint 、 item_in_hand_entity_alphatest_color 、 item_in_hand_glint
|
||||
|
||||
用于各种手持物品的渲染
|
||||
|
||||
#### moving_block 、 moving_block_seasons 、 moving_block_alpha_seasons 、 moving_block_alpha_single_side 、 moving_block_alpha 、 moving_block_double_side 、 moving_block_blend
|
||||
|
||||
用于渲染会动态变化的方块
|
||||
|
||||
### 网易扩展 entity.material 材质文件内容
|
||||
|
||||
文件位于data、resource_packs、vanilla_netease、materials目录下,主要是添加了骨骼模型渲染用的材质
|
||||
|
||||
#### 带有vip字眼的资源
|
||||
|
||||
通常为会员材质中使用到的资源,一般效果较好,Shader实现较复杂,可供学习参考
|
||||
|
||||
#### entity_for_skeleton 、 entity_for_skeleton_cpu
|
||||
|
||||
用于渲染普通的不透明骨骼模型
|
||||
|
||||
#### entity_for_skeleton_hide_cpu 、 entity_for_skeleton_hide
|
||||
|
||||
用于表现实体隐藏状态,效果是渲染纯色的半透明模型
|
||||
|
||||
#### entity_for_skeleton_alpha_cpu 、 entity_for_skeleton_alpha
|
||||
|
||||
用于渲染带透明度的骨骼模型
|
||||
|
||||
#### entity_for_skeleton_bright 、 entity_for_skeleton_bloom 、 entity_for_skeleton_glint 、 entity_for_skeleton_bloom_glint
|
||||
|
||||
某些骨骼模型会使用到各种特殊效果,例如高光,辉光,扫光效果。
|
||||
|
||||
#### entity_for_skeleton_frame_ani
|
||||
|
||||
用于实现骨骼模型序列帧动画
|
||||
|
||||
#### entity_for_skeleton_particle 、 entity_for_skeleton_alpha_particle 、 entity_for_skeleton_bloom_particle 、 entity_for_skeleton_bloom_glint_particle 、 entity_for_skeleton_bright_particle 、 entity_for_skeleton_frame_ani_particle 、 entity_for_skeleton_glint_particle 、 entity_for_skeleton_hide_particle
|
||||
|
||||
在上诉5种类型的材质下增加了用于粒子系统的Shader宏定义,可以让粒子系统发射出带有该材质的骨骼模型。
|
||||
|
||||
## 粒子
|
||||
|
||||
### particles.material 材质文件
|
||||
|
||||
#### particles_opaque
|
||||
|
||||
引擎原生的不透明粒子
|
||||
|
||||
#### particles_alpha
|
||||
|
||||
引擎原生的开启了透明度裁剪的粒子
|
||||
|
||||
#### particles_blend
|
||||
|
||||
引擎原生的带透明度的粒子
|
||||
|
||||
#### particles_effects
|
||||
|
||||
引擎原生带有特效UV变动效果的开启了透明度裁剪的粒子
|
||||
|
||||
#### common_particle、common_particle_add、common_particle_add_texture、common_particle_blend、common_particle_blend_texture
|
||||
|
||||
网易粒子系统,自定义粒子特效基本都是使用这些,功能与上述原生粒子相对应。
|
||||
|
||||
## 阴影
|
||||
|
||||
### shadows.material 材质文件
|
||||
|
||||
阴影的渲染使用了Stencil蒙版技术
|
||||
|
||||
#### shadow_front 、 shadow_back
|
||||
|
||||
在蒙板标记阴影的位置,未进行实际渲染
|
||||
|
||||
### shadow_overlay
|
||||
|
||||
对蒙板中被标记的位置进行真正的渲染
|
||||
|
||||
## UI
|
||||
|
||||
### ui3D.material 材质文件
|
||||
|
||||
包含场景中对象相关的一些特殊UI,或者天气相关UI的材质
|
||||
|
||||
#### selection_XXX
|
||||
|
||||
带有selection字眼的基本上都是选中方块或者实体后这个选中效果的渲染
|
||||
|
||||
#### selection_box
|
||||
|
||||
开启轮廓选择后指向某个对象会显示线框
|
||||
|
||||
#### name_tag、name_tag_depth_tested
|
||||
|
||||
实体头顶名字背景
|
||||
|
||||
#### sign_text、name_text_depth_tested
|
||||
|
||||
实体头顶名字文字
|
||||
|
||||
#### rain
|
||||
|
||||
雨
|
||||
|
||||
#### snow
|
||||
|
||||
雪
|
||||
|
||||
#### lightning
|
||||
|
||||
闪电
|
||||
|
||||
### ui.material 材质文件
|
||||
|
||||
UI界面上使用的UI材质
|
||||
|
||||
由于很多UI会使用相同的材质,每个材质可能会在多处进行使用,这里不一一列举每一处地方,这里只举例几个常见UI界面对象使用到的材质
|
||||
|
||||
#### 物品快捷栏
|
||||
|
||||
ui_textured_and_glcolor
|
||||
|
||||
#### 摇杆,上方的菜单按钮,右上方的移动,潜行,飞行等按钮,右下角的跳跃按钮
|
||||
|
||||
ui_texture_and_color
|
||||
|
||||
#### 背包或物品快捷栏中的物品图标
|
||||
|
||||
ui_item
|
||||
|
||||
#### 屏幕中心的十字光标
|
||||
|
||||
ui_crosshair
|
||||
|
||||
#### 加载场景的背景图
|
||||
|
||||
ui_cubemap
|
||||
|
||||
#### UI上的文字
|
||||
|
||||
ui_text
|
||||
|
||||
629
mcguide/16-美术/7-材质与着色器/3-材质配置说明.md
Normal file
@@ -0,0 +1,629 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 20分钟
|
||||
---
|
||||
|
||||
# 材质配置说明
|
||||
|
||||
## 前言
|
||||
|
||||
本文将详细介绍材质文件的结构与配置方式。
|
||||
|
||||
## 材质文件的加载
|
||||
|
||||
材质文件存放于资源包的materials文件夹下,我们打开resource_packs\vanilla\materials目录,可看到包含有下面这些文件,这些是微软原生定义的材质文件:
|
||||
|
||||

|
||||
|
||||
而resource_packs\vanilla_netease\materials目录下的材质文件,则是网易对原生材质文件的修改与扩充。
|
||||
|
||||
下面我们就先以原生微软的材质文件进行讲解,首先,目录下面的文件基本都是以".material"为后缀的文件,除此之外,还有3个重要的json文件,分别是common.json,fancy.json,sad.json。
|
||||
|
||||
我们先来看看sad.json和fancy.json,他们是用于控制画质表现的,内部各自定义了一个材质文件列表,fancy.json通常比sad.json多定义几个材质文件,并且会为某些材质文件多添加一些额外的宏,这些宏会使shader采用不同的代码块,实现对画质和性能的控制:
|
||||
```json
|
||||
[ // sad.json
|
||||
{"path":"materials/sad.material"},
|
||||
{"path":"materials/entity.material"},
|
||||
{"path":"materials/terrain.material"},
|
||||
{"path":"materials/portal.material"},
|
||||
{"path":"materials/barrier.material"},
|
||||
{"path":"materials/wireframe.material"}
|
||||
]
|
||||
```
|
||||
```json
|
||||
[ // fancy.json
|
||||
{"path":"materials/fancy.material", "+defines":["FANCY"]},
|
||||
{"path":"materials/entity.material", "+defines":["FANCY"]},
|
||||
{"path":"materials/terrain.material", "+defines":["FANCY"]},
|
||||
{"path":"materials/hologram.material"},
|
||||
{"path":"materials/portal.material", "+defines":["FANCY"]},
|
||||
{"path":"materials/barrier.material"},
|
||||
{"path":"materials/wireframe.material"}
|
||||
]
|
||||
```
|
||||
fancy.json相比sad.json多定义了fancy和hologram两个材质文件,并且为某些材质文件内的所有材质增加了`FANCY`宏。开发者可以在shader中通过`FANCY`宏来选择不同的计算策略:
|
||||
```glsl
|
||||
#ifdef FANCY
|
||||
// 完整版效果
|
||||
#else
|
||||
// 青春版效果
|
||||
#endif
|
||||
```
|
||||
通过游戏内的`设置/视频/精美图像`开关可以实现fancy材质和sad材质的切换。当`精美图像`开关打开时,fancy.json中的材质文件就会生效,反之sad.json中的材质文件就会生效:
|
||||

|
||||
|
||||
为了实现更好的表现效果,fancy.json中的材质通常使用较复杂的运算,而sad.json中的材质则通过牺牲渲染表现来换取更好的性能。
|
||||
开发者若需要编写开销较大的材质shader,可以考虑利用`FANCY`宏同时编写一个低消耗的sad版本,然后分别把材质分别放在fancy与sad内定义的相应的材质文件中,玩家可以在游戏中通过`精美图像`开关自行控制是否开启相应效果;如果开发者不需要区分材质的性能开销,则可以考虑仅使用common.json,所有在common.json中定义的材质文件都会被加载。
|
||||
|
||||
## 材质定义与引用
|
||||
|
||||
在此我们将简单介绍MC材质是如何被定义与引用的。
|
||||
### 材质定义
|
||||
假设有一个开发者自定义的材质文件`example.material`,它的基本格式框架如下:
|
||||
```json
|
||||
{ // example.material
|
||||
"materials": {
|
||||
"version": "1.0.0", // 表示material文件格式版本 必不可少
|
||||
|
||||
// 材质1
|
||||
"mat_example": {
|
||||
// ...具体的材质定义
|
||||
},
|
||||
|
||||
// 材质2 它引用了mat_example
|
||||
"other_mat:mat_example": {
|
||||
// ...具体的材质定义
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
可以看到,所有的材质定义都被包含在`materials`这个字段内,并且有一个`version`字段也被包含在`materials`内,用来标识这个材质文件的格式版本。我们还可以注意到这样的字符串:`"other_mat:mat_example"`,它表示材质`other_mat`继承自材质`mat_example`,继承后的材质具有父材质的所有属性,在此基础上,子材质`other_mat`还可以新增属性,或者覆盖父材质中已有的属性。
|
||||
接下来看材质`mat_example`的具体配置格式:
|
||||
```json
|
||||
"mat_example": {
|
||||
"vertexShader": "shaders/glsl/mat_example.vertex",
|
||||
"fragmentShader": "shaders/glsl/mat_example.fragment",
|
||||
|
||||
"+defines": [
|
||||
"USE_SKINNING",
|
||||
"USE_OVERLAY",
|
||||
"NETEASE_SKINNING"
|
||||
],
|
||||
"+samplerStates": [
|
||||
{
|
||||
"samplerIndex": 0,
|
||||
"textureFilter": "Point"
|
||||
}
|
||||
],
|
||||
"states": [
|
||||
"Blending",
|
||||
"DisableDepthWrite"
|
||||
]
|
||||
}
|
||||
```
|
||||
以下是对材质`mat_example`配置的解读:
|
||||
* `vertexShader`和`fragmentShader`定义了这个材质使用的顶点着色器和片元着色器的路径,开发者需要自行实现对应的着色器
|
||||
* `+defines`定义了材质启用的宏定义,在shader中这些宏定义将会生效
|
||||
* `+samplerStates`定义了shader接受的采样纹理列表,纹理个数仅为1,其中`samplerIndex`为0表示材质启用的纹理是`TEXTURE_0`,`textureFilter`指定了纹理采样的过滤方式为点过滤/点滤波
|
||||
* `state`定义了材质额外开启的渲染状态,其中的`Blending`表示使用alpha混合,`DisableDepthWrite`表示材质将不会影响屏幕的深度信息。
|
||||
|
||||
更具体的材质配置参数可以参考本文档的其余部分。
|
||||
|
||||
### 材质引用
|
||||
接上节,我们定义了一个名为`mat_example`的自定义材质,根据材质类型不同,引用材质的方式也有所不同。
|
||||
#### 网易骨骼模型引用材质
|
||||
网易骨骼模型配置写在`vanilla_netease/models/netease_models.json`文件中,开发者可以看到类似这样的模型配置段:
|
||||
```json
|
||||
"model_name": {
|
||||
"dy_load": true,
|
||||
"mesh": "mesh/model_name_mesh.json",
|
||||
"skeleton": "skeleton/model_name_skeleton.json"
|
||||
}
|
||||
```
|
||||
这段配置没有给出引用的材质名,因此游戏会在渲染这个模型时默认使用`entity_for_skeleton`材质,我们可以增加一个`material`字段,显式地引用我们在`entity.material`文件中自定义的材质:
|
||||
```json
|
||||
"model_name": {
|
||||
"dy_load": true,
|
||||
"mesh": "mesh/model_name_mesh.json",
|
||||
"skeleton": "skeleton/model_name_skeleton.json",
|
||||
"material": "mat_example"
|
||||
}
|
||||
```
|
||||
除此之外,我们还支持开发者使用多pass特性来加强骨骼模型的渲染效果。假设开发者希望让一个模型在单帧内使用不同材质各渲染一次,则可以将`material`字段值改为数组:
|
||||
```json
|
||||
"material": ["mat0", "mat1", "mat2"]
|
||||
```
|
||||
该模型将在每一帧**按顺序**渲染数组中所引用的材质。
|
||||
在游戏内调取出这个模型,就可以预览到自定义材质的效果。
|
||||
|
||||
> 骨骼模型所引用的材质,其着色器需要支持`NETEASE_SKINNING`宏的功能,具体可以参考`entity_for_skeleton`材质的着色器实现。
|
||||
|
||||
#### 原版模型引用材质
|
||||
在实体配置文件`vanilla/entity/xxx.entity.json`中,我们可以找到与材质有关的配置内容:
|
||||
```json
|
||||
{
|
||||
"format_version": "1.8.0",
|
||||
"minecraft:client_entity": {
|
||||
"description": {
|
||||
"identifier": "netease:xxx",
|
||||
"materials": {
|
||||
"default": "xxxxx",
|
||||
... // 其他材质配置
|
||||
},
|
||||
... // 其他字段
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
其中`materials`中的`default`字段,就指定了这个实体在常态下使用的材质,我们可以将这个字段的值修改为我们自定义的材质,让这个实体在渲染时具有我们自定义的效果:
|
||||
```json
|
||||
"default": "mat_example"
|
||||
```
|
||||
> 实体模型材质使用的着色器,需要支持`USE_SKINNING`宏,具体可以参考`entity_static_netease`或者`entity_static`材质的实现。
|
||||
|
||||
#### 后处理引用材质
|
||||
开发者可以在`vanilla_netease/graphics_settings/post_process.json`找到后处理相关配置,在配置文件的`process_array`字段下,我们可以看到若干个后处理的定义,在此以老电视机效果作为例子:
|
||||
```json
|
||||
{
|
||||
"name": "oldtv",
|
||||
"enable": false,
|
||||
"paras": [
|
||||
{ "name": "density", "value": 0.1, "range": [0.0, 1.0] },
|
||||
{ "name": "strength", "value": 1.0, "range": [0.0, 1.0] },
|
||||
{ "name": "snow_size", "value": 2.0, "range": [0.5, 16.0] },
|
||||
{ "name": "noise_fps", "value": 6.0, "range": [0.01, 64.0] },
|
||||
{ "name": "black_zone", "value": 0.2, "range": [0.0, 1.0] }
|
||||
],
|
||||
"pass_array":[ // 后处理pass数组
|
||||
{ // 第0个pass
|
||||
"render_target":{
|
||||
"width":1.0,
|
||||
"height":1.0
|
||||
},
|
||||
"material":"old_tv" // 第0个pass使用的材质
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
该配置定义了后处理的名称、默认是否开启、着色器参数、后处理pass数组等信息。可以看到,后处理pass数组中的单个pass包含一个`material`字段,这代表了这个pass将使用的材质,我们可以修改它的值,改变这个pass使用的材质。
|
||||
|
||||
> 制作后处理材质需要定义某些特殊的材质配置,并且着色器要基于全屏范围进行计算,具体可以参考`vanilla_netease/materials/postprocess.material`中的`oldtv`材质
|
||||
|
||||
#### 中国版粒子特效引用材质
|
||||
中国版粒子特效可以使用网易MCStudio生成制作。中国版粒子特效具有默认材质,因此在特效配置中并没有显式地记录引用材质的名称。开发者可以在中国版粒子特效中,加入如下字段来改变粒子特效所引用的材质:
|
||||
```json
|
||||
"materialname": {
|
||||
"value": "my_material"
|
||||
}
|
||||
```
|
||||
|
||||
## 材质属性
|
||||
|
||||
我们把所有字段按功能分到以下几类中:
|
||||
|
||||
### 渲染状态
|
||||
|
||||
#### states
|
||||
|
||||
配置渲染环境,可以有以下的值:
|
||||
```json
|
||||
EnableAlphaToCoverage :半透明对象顺序无关渲染方式的一种,支持MSAA的环境下这个开关才有用,开启后物体边缘会根据透明度作更精确的柔和和过渡,也可用于有大量网格交错重叠的一些复杂场景。
|
||||
Wireframe : 绘制线框模式
|
||||
Blending : 开启颜色混合模式,常用于渲染半透明对象。声明这个之后通常也需要声明混合因子blendSrc,blendDst
|
||||
|
||||
DisableColorWrite : 不往颜色缓冲区写入颜色值,RGBA通道均不写入
|
||||
DisableAlphaWrite : 不往颜色缓冲区写入透明度alpha值,允许写入RGB值
|
||||
DisableRGBWrite : 不往颜色缓冲区写入透明度RGB值,允许写入Alpha值
|
||||
|
||||
DisableDepthTest : 关闭深度测试
|
||||
DisableDepthWrite : 关闭深度写入
|
||||
|
||||
DisableCulling : 不开启背面剔除,会使模型的三角形无论朝向都会被渲染出来。默认情况下会开启背面剔除,即没有面朝相机的三角形会被剔除。
|
||||
InvertCulling :开启正面剔除并且禁用背面剔除,使得朝向相机的三角形被剔除。
|
||||
|
||||
StencilWrite :开启蒙版写入
|
||||
EnableStencilTest : 开启蒙版测试
|
||||
```
|
||||
|
||||
### 着色器路径
|
||||
|
||||
#### vertexShader
|
||||
|
||||
顶点着色器的路径,通常为shaders/glsl/XXX.vertex。
|
||||
|
||||
#### vrGeometryShader 或 geometryShader
|
||||
|
||||
几何着色器的路径,通常为shaders/glsl/XXX.geometry,移动端用不到,无须考虑修改。
|
||||
|
||||
#### fragmentShader
|
||||
|
||||
片段着色器的路径,通常为shaders/glsl/XXX.fragment。
|
||||
|
||||
### Shader宏定义
|
||||
|
||||
#### defines
|
||||
|
||||
为使用到的Shader定义宏。为了代码的复用,我们很多不同的材质会使用相同的shader。此时若希望shader里面某处根据当前材质执行不一样的逻辑,则可以通过材质defines声明的宏去做判断。
|
||||
我们可以用`entity_for_skeleton`这个材质为例,它定义了USE_SKINNING,USE_OVERLAY,NETEASE_SKINNING三个宏。
|
||||
```json
|
||||
"+defines": [ "USE_SKINNING", "USE_OVERLAY", "NETEASE_SKINNING" ]
|
||||
```
|
||||
于是在着色器可以中通过`#ifdef`或`#if`等语句来对宏进行判断并执行不同的逻辑,宏的判断语句是编译期处理的,性能不会因分支而下降:
|
||||
```json
|
||||
#ifdef NETEASE_SKINNING
|
||||
MAT4 boneMat = transpose(mat3x4ToMat4(BONES_70[int(BONEID_0)]));
|
||||
entitySpacePosition = boneMat * POSITION;
|
||||
entitySpaceNormal = boneMat * NORMAL;
|
||||
#else
|
||||
#if defined(LARGE_VERTEX_SHADER_UNIFORMS)
|
||||
entitySpacePosition = BONES[int(BONEID_0)] * POSITION;
|
||||
entitySpaceNormal = BONES[int(BONEID_0)] * NORMAL;
|
||||
#else
|
||||
entitySpacePosition = BONE * POSITION;
|
||||
entitySpaceNormal = BONE * NORMAL;
|
||||
#endif // defined(LARGE_VERTEX_SHADER_UNIFORMS)
|
||||
#endif // NETEASE_SKINNING
|
||||
```
|
||||
|
||||
### 运行时状态
|
||||
|
||||
#### depth 深度测试
|
||||
|
||||
##### depthFunc
|
||||
|
||||
深度检测通过函数,可以使用以下的值:
|
||||
```json
|
||||
Always : 总是通过
|
||||
Equal : 深度值与缓冲区值相等时通过
|
||||
NotEqual :深度值与缓冲区值不相等时通过
|
||||
Less :深度值小于缓冲区值时通过
|
||||
Greater :深度值大于缓冲区值时通过
|
||||
GreaterEqual :深度值大于等于缓冲区值时通过
|
||||
LessEqual :深度值小于等于缓冲区值时通过
|
||||
```
|
||||
|
||||
相关联的states渲染环境配置:
|
||||
```json
|
||||
DisableDepthTest : 关闭深度测试
|
||||
DisableDepthWrite : 关闭深度写入
|
||||
```
|
||||
|
||||
#### Stencil 蒙版测试
|
||||
|
||||
##### stencilRef
|
||||
与蒙版缓冲区比较或要被写入的值
|
||||
|
||||
##### stencilRefOverride
|
||||
是否使用缓冲区当前的值作为stencilRef,支持0或1:
|
||||
```json
|
||||
1 : 使用配置的stencilRef,若配置了stencilRef则stencilRefOverride自动取1
|
||||
0 : 使用缓冲区当前的值作为stencilRef,此情况下不配置stencilRef
|
||||
```
|
||||
|
||||
##### stencilReadMask
|
||||
蒙版缓冲区的值与stencilRef值在比较前均会先与stencilReadMask进行位与运算
|
||||
|
||||
##### stencilWriteMask
|
||||
stencilRef值在写入蒙版缓冲区前会与stencilWriteMask进行位与运算
|
||||
|
||||
##### frontFace, backFace
|
||||
配置网格正面或反面使用什么蒙版测试函数,另外,判断的顺序为先蒙版检测,再深度检测,需要配置以下操作:
|
||||
```json
|
||||
stencilFunc : stencilRef与蒙版缓冲区比较时使用的方法,支持下面的值:
|
||||
Always : 总是通过
|
||||
Equal : stencilRef与缓冲区值相等时通过
|
||||
NotEqual :stencilRef与缓冲区值不相等时通过
|
||||
Less :stencilRef小于缓冲区值时通过
|
||||
Greater :stencilRef大于缓冲区值时通过
|
||||
GreaterEqual :stencilRef大于等于缓冲区值时通过
|
||||
LessEqual :stencilRef小于等于缓冲区值时通过
|
||||
|
||||
stencilFailOp :stencilFunc比较函数返回失败的时候执行的处理,支持下面的值:
|
||||
Keep : 保留缓冲区原本数值
|
||||
Replace : 往缓冲区写入 stencilRef位与stencilWriteMask 的值
|
||||
|
||||
stencilDepthFailOp : stencilFunc比较函数返回成功, 但深度测试失败的时候执行的处理,支持下面的值:
|
||||
Keep : 保留缓冲区原本数值
|
||||
Replace : 往缓冲区写入 stencilRef位与stencilWriteMask 的值
|
||||
|
||||
stencilPassOp : stencilFunc比较函数返回成功,而且深度测试成功的时候执行的处理,支持下面的值:
|
||||
Keep : 保留缓冲区原本数值
|
||||
Replace : 往缓冲区写入 stencilRef位与stencilWriteMask 的值
|
||||
```
|
||||
|
||||
相关联的states渲染环境配置:
|
||||
```json
|
||||
StencilWrite :开启蒙版写入
|
||||
EnableStencilTest : 开启蒙版测试
|
||||
```
|
||||
|
||||
最后我们看一段例子:
|
||||
```json
|
||||
"shadow_back": {
|
||||
"+states": [
|
||||
"StencilWrite",
|
||||
"DisableColorWrite",
|
||||
"DisableDepthWrite",
|
||||
"InvertCulling",
|
||||
"EnableStencilTest"
|
||||
],
|
||||
|
||||
"frontFace": {
|
||||
"stencilFunc": "Always",
|
||||
"stencilFailOp": "Keep",
|
||||
"stencilDepthFailOp": "Keep",
|
||||
"stencilPassOp": "Replace"
|
||||
},
|
||||
"backFace": {
|
||||
"stencilFunc": "Always",
|
||||
"stencilFailOp": "Keep",
|
||||
"stencilDepthFailOp": "Keep",
|
||||
"stencilPassOp": "Replace"
|
||||
},
|
||||
|
||||
"stencilRef": 1,
|
||||
"stencilReadMask": 255,
|
||||
"stencilWriteMask": 1,
|
||||
|
||||
// 省略其余部分
|
||||
}
|
||||
```
|
||||
例子中`StencilWrite`代表支持蒙版缓冲区的写入,`EnableStencilTest`代表开启蒙版测试,`frontFace`配置了正向片元的模板测试策略,代表正向片元的蒙版测试总是通过(Always),深度测试不通过则保持缓冲区值不变(Keep),若深度测试通过则会将模板缓冲更新为`stencilRef`的值。`backFace`同理。
|
||||
|
||||
#### Blend 半透明对象颜色混合
|
||||
|
||||
半透明对象的渲染需要配置混合因子,最终输出的rgb颜色值 = 当前颜色值 * 源混合因子 + 缓冲区中的颜色值 * 目标混合因子
|
||||
|
||||
##### blendSrc
|
||||
源混合因子
|
||||
|
||||
##### blendDst
|
||||
目标混合因子
|
||||
|
||||
##### alphaSrc
|
||||
计算alpha时的源混合因子,通常不配置取默认值
|
||||
|
||||
##### alphaDst
|
||||
计算alpha时的目标混合因子,通常不配置取默认值
|
||||
|
||||
---
|
||||
|
||||
混合因子总共可以取下面的值:
|
||||
```json
|
||||
DestColor : 缓冲区颜色值
|
||||
SourceColor : 当前颜色值
|
||||
Zero : (0,0,0)
|
||||
One : (1,1,1)
|
||||
OneMinusDestColor : (1,1,1) - 缓冲区颜色值
|
||||
OneMinusSrcColor : (1,1,1) - 当前颜色值
|
||||
SourceAlpha : 当前颜色中的alpha值
|
||||
DestAlpha : 缓冲区颜色中的alpha值
|
||||
OneMinusSrcAlpha : 1 - 当前颜色值中的alpha值
|
||||
```
|
||||
|
||||
在引擎中,默认值为:
|
||||
```json
|
||||
blendSrc :SourceAlpha
|
||||
blendDst :OneMinusSrcAlpha
|
||||
alphaSrc :One
|
||||
alphaDst :OneMinusSrcAlpha
|
||||
```
|
||||
|
||||
相关联的states渲染环境配置:
|
||||
```json
|
||||
Blending : 开启颜色混合模式,常用于渲染半透明对象。声明这个之后通常也需要声明混合因子blendSrc,blendDst
|
||||
DisableColorWrite : 不往颜色缓冲区写入颜色值,RGBA通道均不写入
|
||||
DisableAlphaWrite : 不往颜色缓冲区写入透明度alpha值,允许写入RGB值
|
||||
DisableRGBWrite : 不往颜色缓冲区写入透明度RGB值,允许写入Alpha值
|
||||
```
|
||||
|
||||
#### sample 纹理采样
|
||||
|
||||
##### samplerStates
|
||||
配置采样状态,值为一个列表,根据需要采样的纹理个数为每一个纹理进行配置,通常若顶点属性中声明了UV0, UV1,代表需要采样两个纹理,这里则需要配置两个元素。下面看子元素的定义:
|
||||
```json
|
||||
{
|
||||
"samplerIndex": 0,
|
||||
"textureFilter": "Point",
|
||||
"textureWrap": "Repeat"
|
||||
}
|
||||
```
|
||||
|
||||
每个属性的定义如下:
|
||||
###### samplerIndex
|
||||
数字,代表当前正在设置第几张纹理的属性,由0开始
|
||||
|
||||
###### textureFilter
|
||||
纹理过滤模式(默认为Point),当实际显示的纹理贴图相比于原图进行了放大或缩小时,新的分辨率贴图与原分辨率贴图上像素点的映射关系,可以有以下的值:
|
||||
```json
|
||||
Point : 点采样
|
||||
Bilinear : 双线性采样
|
||||
Trilinear : 三线性采样
|
||||
MipMapBilinear : MipMap双线性采样
|
||||
TexelAA :纹素抗锯齿(不是所有设备都支持,不建议使用)
|
||||
PCF :通过比较函数进行采样(不是所有设备都支持,不建议使用)
|
||||
```
|
||||
|
||||
###### textureWrap
|
||||
纹理包裹模式,控制uv若在[0,1]之外的时候应该采样到什么样的纹理,可以有如下的值:
|
||||
```json
|
||||
Repeat : 重复,即把值求模到[0,1]之间进行采样
|
||||
Clamp : 边缘采样,采样最靠近的边缘的值,即1.1比较靠近1,则取1;-0.1比较靠近0,则取0。
|
||||
```
|
||||
|
||||
|
||||
#### vertex 顶点属性
|
||||
|
||||
##### vertexFields
|
||||
顶点属性,用于声明使用这个材质进行渲染的网格每个顶点保存有什么属性,由美术制作资源的时候决定,可能用到的有以下的值:
|
||||
```json
|
||||
Position : 模型空间坐标
|
||||
Color : 颜色
|
||||
Normal : 法线
|
||||
UV0 :纹理采样坐标
|
||||
UV1 :纹理采样坐标
|
||||
UV2 :纹理采样坐标
|
||||
BoneId0 : 骨骼ID,骨骼模型中用到
|
||||
```
|
||||
|
||||
#### rasterizer 光栅化环境配置
|
||||
|
||||
##### msaaSupport
|
||||
配置MSAA(多重采样抗锯齿)的支持(引擎中的默认值为NonMSAA)
|
||||
```json
|
||||
NonMSAA : 在没有开启MSAA的时候材质允许使用
|
||||
MSAA : 在开启MSAA的时候材质允许使用
|
||||
Both :无论是否开启MSAA,材质都允许使用。通常使用这个值就可以了。
|
||||
```
|
||||
|
||||
##### 深度偏移
|
||||
|
||||
深度偏移主要用于解决z-fighting问题,即当两个物体深度相近,则渲染时可能会出现某些帧显示这个物体,某些帧显示另一个物体这种闪烁现象。深度偏移的原理是把其中一个对象往深度大或者小的方向偏移一下,使他们的深度不再一样。
|
||||
可配置以下四个变量:
|
||||
```json
|
||||
depthBias
|
||||
slopeScaledDepthBias
|
||||
depthBiasOGL
|
||||
slopeScaledDepthBiasOGL
|
||||
```
|
||||
|
||||
具体偏移的深度为:
|
||||
```json
|
||||
offset = (slopeScaledDepthBias * m) + (depthBias * r)
|
||||
```
|
||||
在OGL平台上则为:
|
||||
```json
|
||||
offset = (slopeScaledDepthBiasOGL * m) + (depthBiasOGL * r)
|
||||
```
|
||||
|
||||
m是多边形的深度的斜率(在光栅化阶段计算得出)中的最大值。一个多边形越是与近裁剪面平行,m就越接近0。
|
||||
r是能产生在窗口坐标系的深度值中可分辨的差异的最小值,r是由具体实现OpenGL的平台指定的一个常量。
|
||||
|
||||
---
|
||||
相关联的states渲染环境配置:
|
||||
```json
|
||||
Wireframe : 绘制线框模式
|
||||
DisableCulling : 同时渲染正面和反面
|
||||
InvertCulling :使用正面裁剪。默认情况下为背面裁剪,声明这个之后则渲染背面,裁剪掉正面
|
||||
```
|
||||
|
||||
#### primitive 图元
|
||||
|
||||
##### primitiveMode
|
||||
图元渲染模式(引擎中的默认值为TriangleList):
|
||||
```json
|
||||
None : 不渲染,正常情况下不会为这个值
|
||||
QuadList :四边形模式
|
||||
TriangleList : 每三个顶点绘制一个三角形的模式,例如第一个三角形使用顶点v0,v1,v2,第二个使用v3,v4,v5
|
||||
TriangleStrip : 每一个顶点会与前两个出现的顶点构成三角形,结构复杂一点,但会节省数据量
|
||||
LineList : 每两个顶点绘制一条线段
|
||||
Line : 每一个顶点会与前一个出现的一个顶点构成线段。
|
||||
```
|
||||
|
||||
### 材质变体
|
||||
|
||||
#### variants
|
||||
用于快速基于大部分相同定义实现多种子材质。看下面entity_static这个实际例子:
|
||||
```json
|
||||
"entity_static": {
|
||||
"vertexShader": "shaders/entity.vertex",
|
||||
"vrGeometryShader": "shaders/entity.geometry",
|
||||
"fragmentShader": "shaders/entity.fragment",
|
||||
"vertexFields": [
|
||||
{ "field": "Position" },
|
||||
{ "field": "Normal" },
|
||||
{ "field": "UV0" }
|
||||
],
|
||||
"variants": [
|
||||
{
|
||||
"skinning": {
|
||||
"+defines": [ "USE_SKINNING" ],
|
||||
"vertexFields": [
|
||||
{ "field": "Position" },
|
||||
{ "field": "BoneId0" },
|
||||
{ "field": "Normal" },
|
||||
{ "field": "UV0" }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"skinning_color": {
|
||||
"+defines": [ "USE_SKINNING", "USE_OVERLAY" ],
|
||||
"+states": [ "Blending" ],
|
||||
"vertexFields": [
|
||||
{ "field": "Position" },
|
||||
{ "field": "BoneId0" },
|
||||
{ "field": "Color" },
|
||||
{ "field": "Normal" },
|
||||
{ "field": "UV0" }
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"msaaSupport": "Both",
|
||||
"+samplerStates": [
|
||||
{
|
||||
"samplerIndex": 0,
|
||||
"textureFilter": "Point"
|
||||
}
|
||||
]
|
||||
},
|
||||
```
|
||||
variants即为材质变体的声明,上述声明了skinning和skinning_color两个子变体,子变体中对外部某些字段进行了改写。实际使用中,相当于快速定义了两个材质,本体和变体间用点"."连接,两个材质分别为`entity_static.skinning`和`entity_static.skinning_color`
|
||||
|
||||
除此之外,后续如果有其它材质继承自entity_static,比如entity_dynamic,则此材质也会同时继承有此两种变体,分别为`entity_dynamic.skinning`和`entity_dynamic.skinning_color`
|
||||
|
||||
|
||||
## 材质合并规则
|
||||
|
||||
不同目录文件中声明了同一材质时的,在加载后会根据以下规则进行合并:
|
||||
1.通常情况下后加载的文件的材质的字段会覆盖之前加载的
|
||||
2.以下字段特殊,除了替换以外,还支持使用"+"添加与使用"-"删除属性的操作:
|
||||
```json
|
||||
defines
|
||||
states
|
||||
samplerStates
|
||||
```
|
||||
|
||||
举一个的例子,例如包体文件中声明了这么一材质(省略无关代码),定义了三个宏:
|
||||
```json
|
||||
"testMat": {
|
||||
"defines": [ "MACRO_1", "MACRO_2", "MACRO_3" ],
|
||||
}
|
||||
```
|
||||
|
||||
此时,一个Mod也声明了此材质,定义了另外三个宏:
|
||||
```json
|
||||
"testMat": {
|
||||
"defines": [ "MACRO_4", "MACRO_5", "MACRO_6" ],
|
||||
}
|
||||
```
|
||||
上述情况下,最终运行时相当于defines字段被覆盖,实际运行时生效的宏只有: MACRO_4, MACRO_5, MACRO_6
|
||||
|
||||
若MOD中定义的时候使用了"+"符号:
|
||||
```json
|
||||
"testMat": {
|
||||
"+defines": [ "MACRO_4", "MACRO_5", "MACRO_6" ],
|
||||
}
|
||||
```
|
||||
相当于在原来的基础上添加定义,则实际运行时生效的宏有: MACRO_1, MACRO_2, MACRO_3, MACRO_4, MACRO_5, MACRO_6
|
||||
|
||||
若MOD中定义的时候使用了"-"符号:
|
||||
```json
|
||||
"testMat": {
|
||||
"-defines": [ "MACRO_3"],
|
||||
}
|
||||
```
|
||||
相当于在原来的基础上删除某些定义,则实际运行时生效的宏只有: MACRO_1, MACRO_2
|
||||
|
||||
若多个文件都对同一材质进行了定义,而且分别涉及有覆盖,添加,删除操作,则依次生效的顺序为:
|
||||
先执行所有的覆盖操作,再执行所有的添加操作,最后执行所有的删除操作。
|
||||
|
||||
即如果有其中一个材质文件声明删除MACRO_3操作:
|
||||
```json
|
||||
"testMat": {
|
||||
"-defines": [ "MACRO_3"],
|
||||
}
|
||||
```
|
||||
则无论其它文件怎么覆盖,添加MACRO_3,最终合成后这一个材质一定不会有MACRO_3宏。
|
||||
586
mcguide/16-美术/7-材质与着色器/4-材质实战.md
Normal file
@@ -0,0 +1,586 @@
|
||||
---
|
||||
front: https://nie.res.netease.com/r/pic/20220408/3541655a-7205-4609-b118-be8d6dbb84bd.png
|
||||
hard: 入门
|
||||
time: 15分钟
|
||||
selection: 38
|
||||
---
|
||||
|
||||
# 材质实战
|
||||
|
||||
## 前言
|
||||
|
||||
本文将举例介绍在《我的世界》中如何自定义材质。
|
||||
|
||||
## 自定义模型材质
|
||||
MC支持自定义模型材质,包括原版实体材质以及网易骨骼模型材质,开发者可以参考之前的文档说明,结合已有的材质开发出自定义的材质效果。
|
||||
* 原版实体材质可以参考`vanilla/materials/entity.material`下的`entity_static`材质族配置以及shader实现
|
||||
* 网易骨骼模型材质可以参考`vanilla_netease/materials/entity.material`下的`entity_for_skeleton`材质族配置与shader实现
|
||||
|
||||
### 原版模型材质示例 - 玩家反色
|
||||
本节将讨论如何一步步实现并使用玩家角色的反色材质,其效果大致如下:
|
||||

|
||||
图左是反色后的史蒂夫,图右是原史蒂夫。
|
||||
|
||||
#### 材质定义与配置流程<a name="matdef0"></a>
|
||||
我们在`vanilla_netease/entity/player.entity.json`中可以看到玩家实体的相关配置,其中就包含了材质的引用信息:
|
||||
```json
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"minecraft:client_entity": {
|
||||
"description": {
|
||||
"identifier": "minecraft:player",
|
||||
"materials": {
|
||||
"default": "entity_alphatest_netease", // 玩家默认材质名
|
||||
... // 其他材质属性
|
||||
},
|
||||
... // 其他属性
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
一般而言,玩家实体使用`default`字段值中的材质来进行渲染,也就是`entity_alphatest_netease`,我们可以在`vanilla_netease/materials/entity.material`文件中找到该材质:
|
||||
```json
|
||||
"entity_alphatest_netease:entity_nocull_netease": {
|
||||
"+defines": [
|
||||
"ALPHA_TEST"
|
||||
],
|
||||
"+samplerStates": [
|
||||
{
|
||||
"samplerIndex": 1,
|
||||
"textureWrap": "Repeat"
|
||||
}
|
||||
],
|
||||
"msaaSupport": "Both"
|
||||
}
|
||||
```
|
||||
第一行表示`entity_alphatest_netease`材质继承自`entity_nocull_netease`材质。我们希望自定义一个反色材质,可以考虑复用现有材质的大部分属性,仅修改片元着色器的颜色输出,因此可以在`entity.material`中新增这样的材质定义:
|
||||
```json
|
||||
"entity_alphatest_netease_reverse:entity_alphatest_netease": {
|
||||
"fragmentShader": "shaders/glsl/entity_reverse.fragment"
|
||||
}
|
||||
```
|
||||
它表示我们新增了一个名为`entity_alphatest_netease_reverse`的材质,并且复用了`entity_alphatest_netease`材质的属性,其中的片元着色器路径被我们自定义为我们将要实现的片元着色器文件路径。
|
||||
回到玩家实体定义的文件`vanilla_netease/entity/player.entity.json`,我们还需要将刚才的玩家默认材质改为我们自定义的材质:
|
||||
```json
|
||||
"materials": {
|
||||
"default": "entity_alphatest_netease_reverse",
|
||||
... // 其他材质
|
||||
}
|
||||
```
|
||||
这样一来,在材质文件中定义,在玩家实体配置中引用,就完成了玩家材质自定义相关的配置与使用流程,其他非玩家的原版材质也可以使用类似的思路来进行自定义。
|
||||
>如果希望在游戏内动态修改玩家或者实体材质,我们可以使用AddPlayerRenderMaterial接口或者AddActorRenderMaterial接口,具体可以参考我们的API接口文档。
|
||||
|
||||
#### 顶点着色器实现
|
||||
我们仅需要使玩家表现为反色,因此顶点着色器可以直接复用父材质的顶点着色器(entity.vertex),不需要进行任何修改。
|
||||
|
||||
#### 片元着色器实现
|
||||
找到`vanilla_netease\shaders\glsl\entity.fragment`文件,它是游戏内大多数实体材质所使用的片元着色器文件。将它复制一份,重命名为我们在材质定义中所写的名称`entity_reverse.fragment`,然后在最后的颜色输出代码处修改如下:
|
||||
```glsl
|
||||
color.rgb = vec3(1.0) - color.rgb; // 将颜色RGB通道取反色
|
||||
gl_FragColor = color;
|
||||
```
|
||||
运行游戏进入存档,就可以看到类似开头示例图中的景象,玩家实体颜色被替换为了相反色。
|
||||
|
||||
### 骨骼模型材质示例 - Blinn-Phong光照
|
||||
|
||||
我们可以考虑给网易骨骼模型加上Blinn-Phong光照,该光照的具体实现原理可以参考[这里](https://learnopengl-cn.github.io/05%20Advanced%20Lighting/01%20Advanced%20Lighting/#blinn-phong),效果如下:
|
||||
|
||||

|
||||
|
||||
#### 材质定义与配置流程
|
||||
在骨骼模型配置文件`vanilla_netease\models\netease_models.json`中可以看到各个骨骼模型的配置,假设开发者拥有一个骨骼模型,其在`netease_models.json`中的配置如下:
|
||||
```json
|
||||
"model_example": {
|
||||
"dy_load": true,
|
||||
"mesh": "mesh/model_example_mesh.json",
|
||||
"skeleton": "skeleton/model_example_skeleton.json"
|
||||
}
|
||||
```
|
||||
我们希望这个模型能够使用自定义的Blinn-Phong材质,因此我们在这个模型配置中加入对材质的引用信息,表示我们需要让这个骨骼模型使用自定义材质`my_blinn_phong`:
|
||||
```json
|
||||
"model_example": {
|
||||
"dy_load": true,
|
||||
"mesh": "mesh/model_example_mesh.json",
|
||||
"skeleton": "skeleton/model_example_skeleton.json",
|
||||
"material": "my_blinn_phong" // ***添加材质引用信息***
|
||||
}
|
||||
```
|
||||
此时我们还需要定义材质,找到`vanilla_netease/materials/entity.material`文件,加入以下材质定义到`materials`字段内:
|
||||
```json
|
||||
"my_blinn_phong:entity_for_skeleton": {
|
||||
"fragmentShader": "shaders/glsl/my_blinn_phong.fragment",
|
||||
"vertexShader": "shaders/glsl/my_blinn_phong.vertex"
|
||||
}
|
||||
```
|
||||
与[原版材质配置的材质定义](#matdef0)中的例子类似,我们定义一个名为`my_blinn_phong`的材质,继承自`entity_for_skeleton`材质,并且覆盖它的顶点着色器与片元着色器路径。至此,骨骼模型的材质定义与引用就完成了,接下来将讨论如何实现这个材质。
|
||||
>同样地,骨骼模型也有动态替换材质的接口:SetModelMaterial,具体可以参考API文档中的说明。
|
||||
|
||||
#### 顶点着色器实现
|
||||
* 复制一份`netease_entity_skeleton.vertex`代码并重命名,得到待修改的代码模板。
|
||||
* 在main函数以外的地方定义`positionWS`与`normalWS`两个varying,用于做后面的片元着色计算:
|
||||
```glsl
|
||||
varying vec3 positionWS; // 模型世界位置
|
||||
varying vec3 normalWS; // 模型世界法线
|
||||
```
|
||||
* 不同设备的默认精度不同,为了保证计算结果精确可控,我们在开头声明浮点数的精度为`highp`:
|
||||
```glsl
|
||||
precision highp float;
|
||||
```
|
||||
* 在main函数中计算`positionWS`与`normalWS`这2个变量的值:
|
||||
```glsl
|
||||
positionWS = (WORLD * entitySpacePosition).xyz;
|
||||
normalWS = normalize((WORLD * entitySpaceNormal).xyz);
|
||||
```
|
||||
完整代码:
|
||||
```glsl
|
||||
// __multiversion__
|
||||
// This signals the loading code to prepend either #version 100 or #version 300 es as apropriate.
|
||||
precision highp float;
|
||||
|
||||
#include "neteaseModelVertexUtil.h"
|
||||
#line 6
|
||||
|
||||
varying vec4 fogColor; // 雾色与雾强度 vec4(r, g, b, 雾强度)
|
||||
varying vec3 positionWS; // 模型世界位置
|
||||
varying vec3 normalWS; // 模型世界法线
|
||||
|
||||
void main() {
|
||||
POS4 entitySpacePosition, entitySpaceNormal;
|
||||
// 计算顶点动画 得到模型空间位置与法线
|
||||
getEntitySpacePositionAndNormal(entitySpacePosition, entitySpaceNormal);
|
||||
|
||||
calculateOverlayColor(); // 受伤变红等效果
|
||||
calculateGlint(); // 计算附魔效果相关变量
|
||||
|
||||
// 包含其他内置宏相关的varying计算
|
||||
neteaseModelCommonVert();
|
||||
|
||||
// 变量uv已经在头文件中预先声明 这里使用TEXCOORD_0作为uv值
|
||||
uv = TEXCOORD_0;
|
||||
// 计算顶点uv 包括MC支持的UV序列帧效果
|
||||
applyUVAnim(uv);
|
||||
|
||||
// 变换到投影空间
|
||||
POS4 projSpacePos = WORLDVIEWPROJ * entitySpacePosition;
|
||||
gl_Position = projSpacePos;
|
||||
|
||||
positionWS = (WORLD * entitySpacePosition).xyz;
|
||||
normalWS = normalize((WORLD * entitySpaceNormal).xyz);
|
||||
|
||||
// 根据投影位置 计算雾的效果
|
||||
fogColor = getFogColor(projSpacePos);
|
||||
}
|
||||
```
|
||||
|
||||
#### 片元着色器实现
|
||||
|
||||
* 复制一份`netease_entity_skeleton.fragment`代码并重命名,得到待修改的代码模板。
|
||||
* 与顶点着色器对应,在main函数以外的地方定义`positionWS`与`normalWS`两个varying:
|
||||
```glsl
|
||||
varying vec3 positionWS; // 世界空间位置
|
||||
varying vec3 normalWS; // 世界空间法线
|
||||
```
|
||||
* 根据`Blinn-Phong`着色模型进行计算,将光照结果与贴图颜色相乘,具体代码见下方完整版
|
||||
* 由于MC并没有真正的实时光源,因此在着色器中假设了一个处于较高处(200.0, 1000.0, 200.0)的纯白色光源
|
||||
|
||||
完整代码:
|
||||
```glsl
|
||||
// __multiversion__
|
||||
// This signals the loading code to prepend either #version 100 or #version 300 es as apropriate.
|
||||
precision highp float;
|
||||
|
||||
#include "neteaseModelFragmentUtil.h"
|
||||
#include "uniformPerFrameConstants.h"
|
||||
#line 7
|
||||
|
||||
LAYOUT_BINDING(0) uniform sampler2D TEXTURE_0;
|
||||
LAYOUT_BINDING(1) uniform sampler2D TEXTURE_1;
|
||||
|
||||
varying vec4 fogColor; // 雾色与雾强度 vec4(r, g, b, 雾强度)
|
||||
varying vec3 positionWS; // 世界空间位置
|
||||
varying vec3 normalWS; // 世界空间法线
|
||||
|
||||
void main() {
|
||||
// 根据uv获取纹理色 如果是用了NO_TEXTURE宏则会返回 1.0
|
||||
vec4 color = getSampledColor(TEXTURE_0, uv);
|
||||
|
||||
// 杂项的内置宏功能
|
||||
neteaseModelCommonFrag(color);
|
||||
|
||||
// 加入受击变红等效果
|
||||
applyOverlayColor(color);
|
||||
|
||||
// 附魔变色效果 使用TEXTURE_1
|
||||
applyGlint(TEXTURE_1, color);
|
||||
|
||||
// 雾效果
|
||||
applyFog(color, fogColor);
|
||||
|
||||
// 光照计算
|
||||
// 高处白光
|
||||
vec3 lightColor = vec3(1.0, 1.0, 1.0);
|
||||
vec3 lightPos = vec3(200.0, 1000.0, 200.0);
|
||||
// 环境光
|
||||
vec3 ambient = vec3(0.2) * lightColor;
|
||||
// 漫反射光
|
||||
vec3 lightDir = normalize(lightPos - positionWS);
|
||||
float diff = max(dot(normalWS, lightDir), 0.0);
|
||||
vec3 diffuse = diff * lightColor;
|
||||
// 镜面反射光
|
||||
vec3 viewDir = normalize(VIEW_POS.xyz - positionWS);
|
||||
// Blinn-Phong 反射光
|
||||
vec3 halfwayDir = normalize(lightDir + viewDir);
|
||||
float spec = pow(max(dot(normalWS, halfwayDir), 0.0), 32.0);
|
||||
vec3 specular = lightColor * spec;
|
||||
color.rgb *= clamp(diffuse + specular + ambient, 0.3, 1.0);
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
```
|
||||
#### 调出模型
|
||||
打开游戏,使用python脚本执行以下代码,将玩家模型变为引用了自定义材质的自定义模型,即可看到开头所示的效果:
|
||||
```python
|
||||
import mod.client.extraClientApi as clientApi
|
||||
comp = clientApi.GetEngineCompFactory().CreateModel(playerId) # playerId为玩家实体ID
|
||||
comp.SetModel("model_example")
|
||||
```
|
||||
|
||||
## 自定义多pass
|
||||
### 配置说明
|
||||
2.8版本开放了自定义多pass的特性,目前开发者可以通过对骨骼模型配置多pass材质,实现更多效果。开发者仅需修改`netease_models.json`中的模型配置,将模型配置中的`material`字段配置成数组即可:
|
||||
```json
|
||||
{ // netease_models.json
|
||||
"model_example": {
|
||||
"dy_load": true,
|
||||
"mesh": "mesh/model_example_mesh.json",
|
||||
"skeleton": "skeleton/model_example_skeleton.json",
|
||||
"material": ["entity_for_skeleton", "netease_drawline_example"] // ****多pass材质数组****
|
||||
}
|
||||
}
|
||||
```
|
||||
可以看到模型`model_example`中的`material`字段被配置为了由两个材质组成的数组,第一个材质是网易骨骼模型材质`entity_for_skeleton`,第二个材质是2.8新增的内置描边材质`netease_drawline_example`。
|
||||
在渲染模型时,引擎会**按顺序**渲染配置中的材质数组,在这个例子中,模型`model_example`会首先使用材质`entity_for_skeleton`渲染一次,再使用材质`netease_drawline_example`渲染一次。
|
||||
### 示例 - 描边效果
|
||||
描边效果一般用来表示玩家被选中,或者提供高亮效果,可以使用多pass的特性来实现。
|
||||

|
||||
#### 描边材质定义
|
||||
开发者可以在`vanilla_netease/materials/entity.material`中找到新增的内置描边材质`netease_drawline_example`定义:
|
||||
```json
|
||||
"netease_drawline_example:entity_for_skeleton": {
|
||||
"+states": [
|
||||
"InvertCulling"
|
||||
],
|
||||
"vertexShader": "shaders/tutorial/glsl/netease_drawline.vertex",
|
||||
"fragmentShader": "shaders/tutorial/glsl/netease_drawline.fragment"
|
||||
}
|
||||
```
|
||||
材质首先启用了逆(invert)反向剔除,也就是`正向剔除`,然后重新指定了描边shader代码路径。
|
||||
```
|
||||
反向剔除(Back-face Culling)是一种节省渲染开销的技术,其原理是将没有面朝相机的三角面抛弃,不进行渲染
|
||||
```
|
||||
#### 顶点着色器实现
|
||||
描边材质的着色器比较简单,可以删除多余的计算节省开销,一般只要将顶点沿法线方向外扩,让描边部分超出模型范围一点点即可。在这里我们可以使用 `模型中心(0.0, 0.5, 0.0)` 到 `顶点位置(entitySpacePosition.xyz)` 的方向作为外扩方向:
|
||||
```glsl
|
||||
// __multiversion__
|
||||
// This signals the loading code to prepend either #version 100 or #version 300 es as apropriate.
|
||||
#include "neteaseModelVertexUtil.h"
|
||||
#line 4
|
||||
|
||||
const vec4 DRAWLINE_PARAM = vec4(0.6, 0.8, 1.0, 0.025); // (描边颜色RGB, 描边宽度倍率)
|
||||
varying vec4 fogColor; // 雾色与雾强度 vec4(r, g, b, 雾强度)
|
||||
|
||||
void main() {
|
||||
POS4 entitySpacePosition, entitySpaceNormal;
|
||||
// 计算顶点动画 得到模型空间位置与法线
|
||||
getEntitySpacePositionAndNormal(entitySpacePosition, entitySpaceNormal);
|
||||
|
||||
// 放大轮廓
|
||||
vec3 vertexDir = normalize(entitySpacePosition.xyz - vec3(0.0, 0.5, 0.0));
|
||||
entitySpacePosition.xyz += vertexDir * DRAWLINE_PARAM.a;
|
||||
|
||||
// 变换到投影空间
|
||||
POS4 projSpacePos = WORLDVIEWPROJ * entitySpacePosition;
|
||||
gl_Position = projSpacePos;
|
||||
|
||||
// 根据投影位置 计算雾的效果
|
||||
fogColor = getFogColor(projSpacePos);
|
||||
}
|
||||
```
|
||||
#### 片元着色器实现
|
||||
在片元着色阶段,我们可以只输出一个纯色:
|
||||
```glsl
|
||||
// __multiversion__
|
||||
// This signals the loading code to prepend either #version 100 or #version 300 es as apropriate.
|
||||
#include "neteaseModelFragmentUtil.h"
|
||||
#line 4
|
||||
|
||||
const vec4 DRAWLINE_PARAM = vec4(0.6, 0.8, 1.0, 0.025); // (描边颜色RGB, 描边宽度倍率)
|
||||
varying vec4 fogColor;
|
||||
|
||||
void main() {
|
||||
vec4 color = vec4(DRAWLINE_PARAM.rgb, 1.0);
|
||||
|
||||
// 雾效果
|
||||
applyFog(color, fogColor);
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
```
|
||||
>代码中的`DRAWLINE_PARAM`可以改为`EXTRA_VECTOR`,并使用`SetExtraUniformValue`接口动态实现动态修改描边颜色
|
||||
|
||||
## 自定义后处理
|
||||
### 介绍
|
||||
2.8新增了自定义后处理特性,开发者可以参考文档说明,结合已有的内置后处理实现,增加自定义后处理效果。
|
||||
后处理是在场景渲染完毕后额外的全屏渲染流程,通常需要根据当前的渲染结果(深度、颜色以及法线等),额外增加特殊的渲染处理,例如景深、滤镜、bloom、色调映射等效果。
|
||||
### 配置说明
|
||||
要新增自定义的后处理效果,就需要定义后处理所使用的材质。在`postprocess.materal`中,可以自定义后处理专属的材质,其定义方式与其他材质类似,也可以仿照内置的后处理材质配置来实现。在graphics_settings文件夹下的`post_process.json`内,我们可以配置自定义后处理,大致格式如下:
|
||||
```json
|
||||
{ // post_process.json
|
||||
"render_order": [ // 后处理渲染顺序
|
||||
... // 其他内置后处理效果
|
||||
|
||||
// 开发者自定义后处理效果名称
|
||||
"my_post"
|
||||
],
|
||||
"process_array": [ // 后处理定义列表
|
||||
... // 其他内置后处理定义
|
||||
|
||||
// 开发者自定义后处理配置
|
||||
{
|
||||
"name": "my_post",
|
||||
"enable": false,
|
||||
"paras": [
|
||||
{ "name": "param0", "value": 0.1, "range": [0.0, 1.0] },
|
||||
{ "name": "param1", "value": [1.0, 2.0], "range": [0.0, 1.0] },
|
||||
{ "name": "param2", "value": [1.0, 2.0], "range": [0.5, 16.0] }
|
||||
],
|
||||
"pass_array":[
|
||||
{
|
||||
"render_target":{"width":1.0, "height":1.0},
|
||||
"material":"my_post_material",
|
||||
"bindTexture": [
|
||||
{
|
||||
"texture": "textures/postprocess/my_post_texture0"
|
||||
},
|
||||
{
|
||||
"texture": "textures/postprocess/my_post_texture1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
以下是对于这个配置的解读:
|
||||
* `render_order`是后处理渲染顺序表,表示各个后处理效果的先后渲染顺序。
|
||||
* `process_array`包含了各个后处理效果的定义,其中`name`表示后处理名称,与`render_order`字段中的名称对应,`enable`表示游戏内是否默认开启。
|
||||
* `paras`定义了后处理shader中使用到的自定义参数名称、值以及值的范围。自定义参数可以是`float` `vec2` `vec3` `vec4`四种类型,这取决于它们的`value`字段值。例如,`param0`是`float`类型,因为它的`value`字段值是一个数,而`param1`和`param2`是`vec2`类型,因为它们的`value`字段值都是长度为2的数组。
|
||||
* pass_array:后处理pass数组,每个pass包含一个`material`以及`render_target`字段。自定义后处理pass额外支持了`bindTexture`字段,用于更灵活的使用外部纹理信息。配置的贴图按照顺序依次绑定至[3,7]号纹理单元槽。
|
||||
* 渲染自定义后处理效果时,`paras`字段的自定义参数会按照顺序排列,并通过名为`EXTRA_VECTORx`的四维向量传递给shader使用(末尾的x是1到4的整数)。在本例中,自定义参数的总长度为1+2+2=5,因此游戏会使用两个vec4参数传递给shader:
|
||||
```glsl
|
||||
uniform vec4 EXTRA_VECTOR1; // vec4(param0, param1.x, param1.y, param2.x)
|
||||
uniform vec4 EXTRA_VECTOR2; // vec4(param2.y, ?, ?, ?) // 问号表示冗余 不确定值
|
||||
```
|
||||
于是开发者就可以通过`EXTRA_VECTOR`编写参数相关的逻辑。关于`EXTRA_VECTOR`与`SetExtraUniformValue`接口的使用可以参考开发指南或MODSDK的其他文档。
|
||||
|
||||
|
||||
|
||||
### 自定义后处理中可使用的Shader宏变量
|
||||
|
||||
目前我们设置了多个可在自定义后处理中使用的shader宏变量,他们包含了多种游戏中的环境参数。如下表所示。
|
||||
|
||||
| 宏变量名称 | 格式 | 包含参数 | 备注 |
|
||||
| ------------------ | ------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| WORLD_PARAMETERS | vec3(int, float, float) | { 玩家所在维度id,星星亮度,环境光亮度 } | 1. 玩家所在维度id。id=0(主世界),id=1(下界),id=2(末地),id>=3(自定义维度)。<br />2. 星星亮度。该值与<a href="./../../../mcdocs/1-ModAPI/接口/世界/渲染.html#GetStarBrightness">GetStarBrightness接口</a>所返回的值一致。<br />3. 环境光亮度。该值与<a href="./../../../mcdocs/1-ModAPI/接口/世界/渲染.html#GetAmbientBrightness">GetAmbientBrightness接口</a>所返回的值一致。 |
|
||||
| TIME_PARAMETERS | vec2(float, float) | { 自当天日出后流逝的游戏刻数,世界总共流逝的游戏刻数 } | 1. 自当天日出后流逝的游戏刻数,与`/time query daytime`指令所得出值一致。<br />2. 世界总共流逝的游戏刻数,与`/time query worldtime`指令所得出的值一致。 |
|
||||
| WEATHER_PARAMETERS | vec3(int, float, float) | { 天气类型,降雨等级,打雷等级 } | 1. 天气类型。值为0表示晴天,值为1时表示下雨,值为2时表示雷暴。<br />2. 降雨等级。目前世界的降雨强度,值范围为[0,1]。<br />3. 打雷等级。目前世界的打雷强度,值范围为[0,1]。 |
|
||||
| CAMERA_POSITION | vec3(float, float, float) | { 相机位置 } | 玩家相机的位置。 |
|
||||
| PLAYER_POSITION | vec3(float, float, float) | { 玩家位置 } | 玩家的世界位置。 |
|
||||
| SKY_COLOR | vec3(float, float, float) | { 天空颜色 } | 天空盒的颜色。只有开启了精美天空选项时该值才有效。否则该值为0。 |
|
||||
|
||||
以上宏变量均可在fragment shader或者vertex shader中使用。下面我们使用fragment shader作为示例:
|
||||
|
||||
```glsl
|
||||
// ... 忽略其他shader代码 ...
|
||||
|
||||
uniform vec3 WORLD_PARAMETERS; // (玩家所在维度id,星星亮度,环境光亮度)
|
||||
uniform vec3 PLAYER_POSITION; // (玩家位置)
|
||||
|
||||
void main(){
|
||||
vec4 base_color = texture( TEXTURE_0, uv );
|
||||
if(WORLD_PARAMETERS.x == 1) { // 如果维度是下界,则增加亮度
|
||||
base_color *= 2.0;
|
||||
}
|
||||
if(PLAYER_POSITION.y <= 16) { // 如果玩家位置高度低于16,则改变颜色
|
||||
base_color.r *= 2.0;
|
||||
}
|
||||
gl_FragColor = base_color;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 示例 - 老电视机滤镜
|
||||
该效果名为`oldtv`,是2.8版本新增的内置后处理效果,开发者可以使用IMGUI查看,或者使用后处理的`SetEnableByName`接口开启查看。
|
||||

|
||||
|
||||
#### 后处理定义
|
||||
开发者可以直接参考包体内的`graphics_settings/post_process.json`以及`materials/postprocess.material`中的`old_tv`材质,查看具体的后处理配置以及后处理材质定义。
|
||||
#### 顶点着色器实现
|
||||
直接复用内置的顶点着色器`shaders/glsl/uv_fullscreen.vertex`,是用于全屏渲染的简单顶点着色器:
|
||||
```glsl
|
||||
// __multiversion__
|
||||
// This signals the loading code to prepend either #version 100 or #version 300 es as apropriate.
|
||||
|
||||
#include "vertexVersionCentroidUV.h"
|
||||
#include "uniformWorldConstants.h"
|
||||
|
||||
attribute POS4 POSITION;
|
||||
attribute vec2 TEXCOORD_0;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(POSITION.xyz, 1.0);
|
||||
uv = TEXCOORD_0;
|
||||
}
|
||||
```
|
||||
#### 片元着色器实现
|
||||
具体思路是各种效果的叠加,包括噪声、划痕、色调映射以及黑边等,可以参考包体内的`shaders/tutorial/glsl/old_tv.fragment`代码,在此仅贴出部分实现:
|
||||
```glsl
|
||||
uniform vec4 EXTRA_VECTOR1; // (density 雪花密度, strength 雪花强度, snow_size 雪花大小, noise_fps 噪声频率)
|
||||
uniform vec4 EXTRA_VECTOR2; // (black_zone 黑边占屏幕比率, ?, ?, ?)
|
||||
|
||||
void main(){
|
||||
vec4 base_color = texture( TEXTURE_0, uv );
|
||||
float time_flow = floor(TIME * EXTRA_VECTOR1.a) / EXTRA_VECTOR1.a;
|
||||
|
||||
// 白噪声
|
||||
vec2 snow_uv = floor(uv * vec2(1.6, 0.9) * (2048.0 / EXTRA_VECTOR1.b)) * EXTRA_VECTOR1.b;
|
||||
float r = rand(snow_uv + time_flow);
|
||||
r = step(1.0 - EXTRA_VECTOR1.r, r);
|
||||
|
||||
// 纵向的随机划痕 增加陈旧的观感
|
||||
float glitch = rand(uv.xx * 1024.0 + time_flow);
|
||||
glitch = step(0.999, glitch);
|
||||
|
||||
// 颜色滤镜 叠加灰度感 让画面发旧
|
||||
float gray = dot(base_color.rgb, vec3(0.2126729, 0.7151522, 0.0721750));
|
||||
base_color.rgb = vec3(gray * 0.4) + base_color.rgb * vec3(0.6, 0.55, 0.5);
|
||||
|
||||
// 中心的噪声需要淡出 防止遮挡视野
|
||||
float dist_to_center = length(uv - vec2(0.5, 0.5));
|
||||
dist_to_center *= dist_to_center;
|
||||
|
||||
vec3 final_color = base_color.rgb + vec3((r + glitch) * EXTRA_VECTOR1.g * dist_to_center);
|
||||
|
||||
// 电影黑边
|
||||
float black_zone = step(min(uv.y, 1.0 - uv.y), EXTRA_VECTOR2.r * 0.5);
|
||||
final_color = (1.0 - black_zone) * final_color.rgb;
|
||||
|
||||
gl_FragColor = vec4(final_color, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
### 示例 - 地形扫描效果
|
||||
|
||||
介绍基于MC后处理框架的扫描效果的实现,效果如图。另外,目前在部分移动端受限于精度问题,表现异常。
|
||||
|
||||

|
||||
|
||||
#### scan_map.fragment
|
||||
如下为MC自定义后处理框架下扫描效果的配置。render_order表示渲染顺序,其中的值对应process_array.name的值,process_array.enable表示是否开启,process_array.paras表示可调参数,最后以uniform变量EXTRA_VECTOR1的形式传进片元着色器中。process_array.pass_array.render_target表示渲染范围,此处表示全屏幕,process_array.pass_array.material表示使用的材质,process_array.pass_array.depth_enable表示是否需要深度图,开启后可以在片元着色器中的TEXTURE_2拿到深度图。
|
||||
```json
|
||||
{
|
||||
"render_order": [
|
||||
"scanmap"
|
||||
],
|
||||
"process_array": [
|
||||
{
|
||||
"name": "scanmap",
|
||||
"enable": false,
|
||||
"paras": [
|
||||
{ "name": "color", "value": [1.0, 1.0, 1.0], "range": [0.0, 1.0] },
|
||||
{ "name": "freq", "value": 2.0, "range": [0.0, 10.0] }
|
||||
],
|
||||
"pass_array":[
|
||||
{
|
||||
"render_target":{
|
||||
"width":1.0,
|
||||
"height":1.0
|
||||
},
|
||||
"material":"scan_map",
|
||||
"depth_enable": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
如下为MC后处理框架下扫描效果的片元着色器。引入fragmentVersionCentroidUV.h头文件是为了拿到纹理对应的uv坐标,引入uniformPerFrameConstants.h头文件是为了其中的TIME变量,引入uniformWorldConstants.h头文件是为了其中的PROJ和WORLDVIEW变量,引入uniformRenderChunkConstants.h头文件是为了其中的CHUNK_ORIGIN_AND_SCALE变量,具体每个uniform变量的作用详见[材质介绍与入门](./1-材质介绍与着色器入门.md)。
|
||||
|
||||
扫描效果大致是根据各点与相机的距离,由近到远地让一定距离范围内的点提高亮度,同时在提亮的区域内划分出格子。其中,各点与相机的距离可以从深度图恢复出线性深度以便计算,由于MC的shader中的坐标系是基于相机为原点的,所以还需要利用CHUNK_ORIGIN_AND_SCALE才能保证网格不随玩家的移动而移动。然后划分格子就是根据世界坐标做一些取余操作,提取出靠近0的部分进行高亮。最后,做一些边缘淡出效果即可。
|
||||
|
||||
```glsl
|
||||
// __multiversion__
|
||||
// This signals the loading code to prepend either #version 100 or #version 300 es as apropriate.
|
||||
#include "fragmentVersionCentroidUV.h"
|
||||
#include "uniformPerFrameConstants.h"
|
||||
#include "uniformWorldConstants.h"
|
||||
#include "uniformRenderChunkConstants.h"
|
||||
#line 7
|
||||
|
||||
LAYOUT_BINDING(0) uniform sampler2D TEXTURE_0;
|
||||
LAYOUT_BINDING(1) uniform sampler2D TEXTURE_1;
|
||||
LAYOUT_BINDING(2) uniform sampler2D TEXTURE_2;
|
||||
|
||||
uniform vec4 EXTRA_VECTOR1; // (red 扫描颜色r, green 扫描颜色g, blue 扫描颜色b, freq 频率)
|
||||
|
||||
highp vec3 get_world_position(vec2 uv, float z) {
|
||||
highp vec4 pos_clip = vec4(uv, z, 1.0) * 2.0 - 1.0;
|
||||
highp vec4 pos_world = inverse(PROJ*WORLDVIEW) * pos_clip;
|
||||
return pos_world.xyz / pos_world.w;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = texture2D(TEXTURE_0, uv);
|
||||
float depth = texture2D(TEXTURE_2, uv).r;
|
||||
|
||||
highp vec3 world_pos = get_world_position(uv, depth);
|
||||
world_pos -= mod(CHUNK_ORIGIN_AND_SCALE.xyz, 1.0);
|
||||
float dist = length(world_pos);
|
||||
|
||||
float peak = mod(TIME * EXTRA_VECTOR1.a * 10.0, 100.0);
|
||||
float fade = 1.0 - clamp((peak - 90.0) / (100.0 - 90.0), 0.0, 1.0);
|
||||
float highlight_area = clamp(1.0 - (abs(dist - peak) * 0.1), 0.0, 1.0) * fade;
|
||||
|
||||
vec3 grid = 1.0 - abs(mod(world_pos, 1.0) * 20.0 - 1.0);
|
||||
float border_area = clamp(max(grid.r, max(grid.g, grid.b)), 0.0, 1.0);
|
||||
|
||||
color.rgb = mix(color.rgb, vec3(EXTRA_VECTOR1.r, EXTRA_VECTOR1.g, EXTRA_VECTOR1.b), highlight_area * border_area);
|
||||
gl_FragColor = color;
|
||||
}
|
||||
```
|
||||
|
||||
## 材质使用示例
|
||||
|
||||
我们提供了一些材质配置使用示例给开发者参考,开发者可到MC Studio的内容库中进行搜索。
|
||||
|
||||
(1)CustomEntityMaterialMod
|
||||
|
||||
该模组演示了如何配置和使用自定义生物的自定义材质的功能。
|
||||
|
||||
(2)CustomPlayerMaterialMod
|
||||
|
||||
该模组演示了如何配置和使用玩家自身(BB模型)的自定义材质的功能。
|
||||
|
||||
(3)CustomSkeletonMaterialMod
|
||||
|
||||
该模组演示了如何配置和使用骨骼模型的自定义材质的功能。
|
||||
|
||||
(4)CustomNeteaseParticlesMod
|
||||
|
||||
该模组演示了如何配置和使用网易版粒子特效的自定义材质的功能。
|
||||
|
||||
(5)CustomPostprocessMod
|
||||
|
||||
该模组演示了如何配置和使用自定义后处理的功能。
|
||||
96
mcguide/16-美术/7-材质与着色器/5-材质相关接口介绍.md
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 20分钟
|
||||
---
|
||||
|
||||
# 材质相关接口介绍
|
||||
|
||||
## 前言
|
||||
本文将简单介绍《我的世界》开放的材质相关python接口,便于开发者加深对材质系统的了解,同时避免开发者因找不到相关接口而缺乏头绪的情况。
|
||||
网易版《我的世界》向开发者开放python接口,其中包括了材质相关的接口,允许开发者对材质进行一定程度的增删改查。通过调用python接口,开发者不仅可以减少配置json的麻烦,还可以实现在游戏内动态替换材质、动态调整材质特性的功能,关于接口的更具体描述,可以前往<a href="./../../../mcdocs/1-ModAPI/接口/Api索引表.html">API文档</a>页面进行查阅。
|
||||
|
||||
## 网易骨骼模型材质
|
||||
### SetModelMaterial
|
||||
设置骨骼模型所使用的材质,允许设置常规材质和低配机材质,并且可以精确到具体的骨骼使用何种材质。
|
||||
|
||||
### SetModelMultiPassMaterial
|
||||
设置骨骼模型所使用的材质数组,用法大致与`SetModelMaterial`接口相同。
|
||||
|
||||
### GetModelMaterial
|
||||
获取骨骼模型所使用的材质,可以精确到骨骼。
|
||||
|
||||
### GetExtraUniformValue
|
||||
获取骨骼模型材质参数,对应shader中的uniform值。
|
||||
|
||||
### SetExtraUniformValue
|
||||
设置骨骼模型材质参数,对应shader中的uniform值。
|
||||
|
||||
## 原版实体与玩家实体材质
|
||||
### AddActorRenderMaterial
|
||||
增加或替换实体的材质,需要配合`RebuildActorRender`接口使用。
|
||||
|
||||
### AddPlayerRenderMaterial
|
||||
增加或替换玩家的材质,需要配合`RebuildPlayerRender`接口使用。
|
||||
|
||||
### AddActorRenderController
|
||||
增加或修改实体的渲染控制器。渲染控制器可以在游戏内根据实体的情况,实时决定实体使用何种材质、纹理以及几何体。
|
||||
|
||||
### AddPlayerRenderController
|
||||
增加或修改玩家的渲染控制器。渲染控制器可以在游戏内根据实体的情况,实时决定实体使用何种材质、纹理以及几何体。
|
||||
|
||||
### RemoveActorRenderController
|
||||
删除实体渲染控制器
|
||||
|
||||
### RemovePlayerRenderController
|
||||
删除玩家渲染控制器
|
||||
|
||||
### AddActorRenderControllerArray
|
||||
添加实体渲染控制器数组中字典arrays元素
|
||||
|
||||
### CopyActorRenderMaterialFromPlayer
|
||||
将玩家的材质引用拷贝到某一类实体上,精确到材质键,例如可以将玩家的`default`对应的材质拷贝到羊的`default`对应的材质。
|
||||
|
||||
### SetEntityExtraUniforms
|
||||
设置实体shader的自定义变量值,包括生物实体以及玩家实体。目前自定义变量值包含四个四维向量,因此开发者可以利用16个float的信息实现对材质表现的控制。
|
||||
|
||||
### GetEntityExtraUniforms
|
||||
获取实体shader的自定义变量值,包括生物实体以及玩家实体。
|
||||
|
||||
### RebuildActorRender
|
||||
重建或刷新实体渲染,一般需要在实体渲染相关的修改操作结束后调用。
|
||||
|
||||
### RebuildPlayerRender
|
||||
重建或刷新玩家实体渲染,一般需要在玩家实体渲染相关的修改操作结束后调用。
|
||||
|
||||
## 方块实体材质
|
||||
### SetBlockEntityExtraUniforms
|
||||
设置实体方块的shader参数值,与`SetEntityExtraUniforms`接口或`SetExtraUniformValue`接口类似。
|
||||
|
||||
### GetBlockEntityExtraUniforms
|
||||
获取实体方块的shader参数值,与`GetEntityExtraUniforms`接口或`GetExtraUniformValue`接口类似。
|
||||
|
||||
## 后处理材质
|
||||
### SetEnableByName
|
||||
根据指定的后处理名称,开关对应的自定义后处理效果。
|
||||
|
||||
### CheckEnabledByName
|
||||
根据指定的后处理名称,获取对应的自定义后处理效果是否开启。
|
||||
|
||||
### SetParameter
|
||||
可以根据后处理效果名称与自定义参数名称,设置自定义后处理的自定义参数,与`GetEntityExtraUniforms`接口或`GetExtraUniformValue`接口类似。
|
||||
|
||||
### GetParameter
|
||||
可以根据后处理效果名称与自定义参数名称,获取自定义后处理的自定义参数值,与`GetEntityExtraUniforms`接口或`GetExtraUniformValue`接口类似。
|
||||
|
||||
### InsertPassToPostprocess
|
||||
允许在自定义后处理pass数组中插入一个自定义pass。
|
||||
|
||||
### PushBackPassToPostprocess
|
||||
允许在自定义后处理pass数组末尾加入一个自定义pass。
|
||||
|
||||
### RemovePassInPostprocess
|
||||
指定后处理名称与下标,可以移除后处理pass数组中的某个pass,该操作不会影响文件。
|
||||
|
||||
### PopBackPassInPostprocess
|
||||
指定后处理名称,可以移除后处理pass数组中的最后一个pass,该操作不会影响文件。
|
||||
206
mcguide/16-美术/7-材质与着色器/6-图形管线与GLSL简单入门.md
Normal file
@@ -0,0 +1,206 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 20分钟
|
||||
---
|
||||
|
||||
# 图形管线与GLSL简单入门
|
||||
|
||||
## 前言
|
||||
本文将简单介绍图形管线、shader与glsl相关内容,并提供一些相关的学习建议。
|
||||
|
||||
## 图形管线与着色器
|
||||
### 什么是图形管线
|
||||
图形管线也叫渲染管线,是将渲染数据(例如模型、材质、贴图等内容)进行整合、运算并最终显示到屏幕的流程。渲染管线将渲染的整个流程划分为了若干个阶段,每一个阶段的计算过程都依赖上一个阶段的计算结果,就像工厂内的生产流水线,每一个环节都有专门的步骤来对原材料进行加工,这就是它被称为渲染管线或渲染流水线的原因。
|
||||
|
||||
### 图形管线阶段
|
||||
图形管线简单而言可以分为`程序阶段`、`几何阶段`以及`光栅化阶段`三大阶段,每一大阶段又由若干个小阶段或者细节构成。
|
||||
#### 程序阶段
|
||||
程序阶段又称为应用阶段,这个阶段是由CPU负责完成的。在这个阶段中,CPU需要处理待渲染的数据,包括加载资源,物理计算,计算相机、光源、场景模型以及人物等对象的位置与动画帧,剔除冗余的渲染数据(可能会使用到**动态加载**、**遮挡剔除**以及**可见性剔除**等技术),并最终将这些数据整理成GPU可理解的形式(位置、法线、uv坐标、切线以及颜色值等),提交给GPU来进行下一个阶段的计算。
|
||||
|
||||
#### 几何阶段
|
||||
程序阶段的数据提交到GPU之后,几何阶段就开始了。渲染数据在几何阶段中主要以几何图形数据的形式存在,在这个阶段中,开发者可以定义用于几何阶段的着色器(顶点着色器、曲面细分着色器、几何着色器等),并且配置几何阶段的相关操作。
|
||||
一般而言,渲染数据在几何阶段会经过**顶点着色**、**曲面细分**、**几何着色**、**裁剪**、**屏幕映射**等过程。
|
||||
`顶点着色`:开发者可以开发**顶点着色器**来自定义这一阶段,一般用于计算顶点动画(skinning)、空间变换、顶点光照以及完成其他逐顶点计算操作。
|
||||
`曲面细分与几何着色`:开发者可以编写曲面细分着色器以及几何着色器来自定义这两个阶段,它们一般用来实现LOD、模型平滑与毛发等涉及顶点增删的操作。
|
||||
`裁剪`:这个阶段用来将相机看不见的渲染数据剔除掉,以节省接下来的运算性能。例如,完全超出屏幕可见范围、没有面向相机或者距离相机极远的三角形都会被直接抛弃,无法进入下一个渲染管线阶段。在默认情况下,渲染管线会开启`背面剔除`,也就是将没有面朝相机的三角形抛弃,仅保留面朝相机的部分,这一操作可以节省后续阶段大约一半的渲染开销。当然,裁剪模式也可以配置为`前面剔除`,也就是保留背对相机的三角形,抛弃面向相机的三角形。
|
||||
`屏幕映射`:在完成了裁剪操作后,GPU会将剩余三角形的各个顶点坐标,转化为屏幕坐标,然后交由接下来的光栅化阶段进行计算。
|
||||
|
||||
#### 光栅化阶段
|
||||
光栅化阶段负责将几何阶段传递过来的几何信息,转换为屏幕上的颜色信息。具体而言,光栅化可以包括这些阶段:
|
||||
`图元组装和三角形遍历`:这两个过程负责处理几何阶段传入的几何数据,整理并计算每一个三角形在屏幕上占据哪些像素,并根据像素位置与三角形顶点的位置关系进行插值,生成**片元**。
|
||||
> 片元是顶点数据经过三角形遍历后的插值结果,同时还额外包含了一些着色器内部定义的信息,可以理解为候选像素。
|
||||
|
||||

|
||||
`片元着色`:开发者可以编写**片元着色器**来自定义这一阶段,片元着色通过结合片元数据(屏幕坐标、uv值、深度值等)和程序给定的数据(例如游戏时间、纹理、光照信息等),并依照一定规则来计算**片元**的颜色与透明度等信息。
|
||||
`片元测试`:在完成片元着色后,所有片元都将经过深度测试、透明度测试、模板测试等测试操作,这些测试分别检查片元深度、片元透明度、片元模板值是否能够满足某个比较条件(大于、等于、小于或者不等于)。如果片元没有通过其中一种测试,那么这个片元会被丢弃,例如透明度为0的片元一般不能够通过透明度测试,因为这个片元会被认为是看不见的。
|
||||
`混合`:完成片元测试之后,管线会根据由远及近的顺序,对单个像素下的所有剩余片元进行透明度混合。透明度混合的计算方式(例如背景色与前景色的权重等)可以由开发者配置。通过透明度混合,我们才能看到常见的水面遮蔽河床,或者有色玻璃覆盖后方的景象:
|
||||

|
||||
|
||||
### 着色器
|
||||
着色器是一系列可以在GPU图形管线上运行的程序的总称,《我的世界》允许使用`顶点着色器`与`片元着色器`进行材质开发,它们分别在**顶点着色阶段**和**片元着色阶段**起作用。
|
||||
|
||||
***顶点着色器***(Vertex Shader)是基于三角形顶点进行计算的。在顶点着色器中,开发者可以拿到一个顶点的位置、法线以及uv等信息,并采用逐顶点的算法,例如顶点动画计算以及逐顶点光照等,最后输出顶点的位置信息以及其他自定义的变量信息。由于游戏中的顶点数量往往都比片元要少得多,因此如果将片元着色器中的耗时计算操作迁移至顶点着色器来进行(例如计算玩家视线方向、计算变换矩阵等),一般都能够使材质的性能得到显著提升,但是涉及表现力的部分会大打折扣。
|
||||

|
||||
上图中,左边为逐顶点光照,右边为逐像素(逐片元)光照,可以看到逐顶点光照的效果不均匀而且粗糙,逐片元光照的效果更为细腻而且均匀。
|
||||
**片元着色器**(Fragment Shader)是基于片元进行计算的。在片元着色器中,开发者可以获取由顶点着色器传递下来的插值信息(uv值、位置信息、颜色值等),并进行逐片元/像素的计算。片元可以视为经过插值之后的候选像素,单次渲染的数量一般比顶点数更高,因此逐顶点的算法能够带来更细致且精确的效果,但是性能开销也比逐顶点计算更高。
|
||||

|
||||
上图为基于片元着色器的法线贴图效果,方块模型本身仅有简单的六个面,但是使用了逐片元的法线贴图,可以使方块表面在视觉上呈现凹凸不平的效果。
|
||||
|
||||
## GLSL入门
|
||||
### 什么是GLSL
|
||||
GLSL全称是OpenGL Shading Language,是基于OpenGL的类C着色语言。开发者可以使用GLSL来编写包括顶点着色器、几何着色器、片元着色器等在内的各种着色器,实现对材质渲染过程的高度定制化。OpenGL目前受到市面上绝大多数的GPU支持,因此使用GLSL进行着色器开发有着跨平台的优势。
|
||||
|
||||
目前MC的渲染(非渲染龙)是基于OpenGL实现的,不同平台的不同设备支持的OpenGL版本各有不同,越高级的OpenGL版本能使用的特性就越多,但因此兼容的设备就越少,开发者应考虑shader所使用的特性是否能够兼容目标设备。
|
||||
|
||||
### GLSL语法基础
|
||||
本节默认开发者对C语言语法有基本的了解,或者有一定的编程基础。
|
||||
#### 类型
|
||||
GLSL的类型以向量、矩阵、纹理为主,常见类型如下:
|
||||
```glsl
|
||||
float | int | void: 与C语言对应的浮点数、整型与空类型
|
||||
vec2 | vec3 | vec4: N维浮点数向量
|
||||
mat2 | mat3 | mat4: NxN浮点数矩阵
|
||||
sampler2D | samplerCube: 2D纹理与立方体纹理
|
||||
```
|
||||
GLSL的类型定义与C语言没有太大的差别:
|
||||
```glsl
|
||||
vec3 v3 = vec3(1.0); // 定义一个全为1.0的三维向量
|
||||
```
|
||||
GLSL允许定义一维数组,但是数组长度应为常量:
|
||||
```glsl
|
||||
float myFloats[3] = float[3](1.0, 2.0, 3.0);
|
||||
```
|
||||
还可以使用结构体,增加代码可读性:
|
||||
```glsl
|
||||
struct MyFloatType{
|
||||
float x;
|
||||
};
|
||||
|
||||
MyFloatType num;
|
||||
```
|
||||
#### 运算
|
||||
在GLSL中,对向量访问的支持有多种方式,比较自由:
|
||||
```glsl
|
||||
vec3 v3;
|
||||
// 以下三个语句都是合法且等价的
|
||||
v3 = vec3(0.0);
|
||||
v3.rgb = vec3(0.0);
|
||||
v3.xyz = vec3(0.0);
|
||||
// 允许对单个分量进行修改和访问
|
||||
v3.x = 1.0;
|
||||
v3.z = v3.x;
|
||||
v3[1] = 7.0; // 数组下标访问 v3[1] == v3.y == v3.g
|
||||
v3.xyz = v3.xxx; // 分量访问 case 0
|
||||
v3.rgb = v3.zyz; // 分量访问 case 1
|
||||
```
|
||||
GLSL支持一些基础的运算:
|
||||
```glsl
|
||||
vec3 a = vec3(0.5);
|
||||
vec3 b = vec3(1.0, 2.0, 3.0);
|
||||
vec3 c = a * b; // vec3(0.5, 1.0, 1.5)
|
||||
vec3 d = b - a; // vec3(0.5, 1.5, 2.5)
|
||||
vec2 e = a.rg * b.xx + c.gr; // 分量访问混搭计算
|
||||
|
||||
// 矩阵计算
|
||||
mat3 ma = ...
|
||||
mat3 mb = ...
|
||||
mat3 mc = ma * mb; // 矩阵相乘
|
||||
vec3 myVector = vec3(1.0);
|
||||
vec3 result = mc * myVector; // 3x3矩阵乘以3x1向量,得到3x1向量
|
||||
```
|
||||
除此之外,GLSL还支持C语言中的比较、逻辑与或非等运算,开发者可以参考网络上的其他教程与文档进行学习。
|
||||
#### 特殊变量
|
||||
GLSL定义了一些内置变量,这些内置变量在顶点着色器和片元着色器中有所不同,以下是常见的内置变量:
|
||||
```glsl
|
||||
// 顶点着色器常见内置变量
|
||||
gl_Position: 顶点着色器的输出变量,开发者需要将顶点坐标的计算结果赋给该变量
|
||||
|
||||
// 片元着色器常见内置变量
|
||||
gl_FragCoord: 记录当前片元的帧缓冲坐标,一般表示屏幕上的像素坐标
|
||||
gl_FragColor: 片元着色器的输出变量,开发者需要将片元着色的颜色结果赋给该变量
|
||||
```
|
||||
GLSL还允许定义`varying`、`uniform`和`attribute`变量,这些变量分别使用对应的关键词来修饰。
|
||||
`varying`变量是用于着色器内部传递数据的变量,一般用于将顶点着色器中的计算结果传递给片元着色器:
|
||||
```glsl
|
||||
// vertex示例
|
||||
varying float intensity; // 顶点着色器开头的varying变量定义
|
||||
intensity = uv.x; // 将uv值的x分量赋给intensity
|
||||
```
|
||||
在片元着色器中就可以拿到这个值:
|
||||
```glsl
|
||||
varying float intensity; // 片元着色器中同样需要定义这个varying变量
|
||||
float myResult = intensity * 2.0; // 利用varying值进行计算
|
||||
```
|
||||
`uniform`变量可以理解为着色器中的全局变量,这类变量一旦被定义,在整个渲染管线中的着色器中都可以被使用。uniform变量的值是CPU通过OpenGL提供的接口设置的,一般用于从CPU传递MVP矩阵、光源信息、时间以及纹理等具有通用性或全局性,且不依赖材质计算结果的数据。
|
||||
```glsl
|
||||
uniform mat4 WORLDVIEWPROJ; // MC中的uniform变量之一,用于将顶点从模型空间变换为透视投影空间
|
||||
gl_Position = WORLDVIEWPROJ * vPos;
|
||||
```
|
||||
`attribute`用于修饰顶点信息变量,只能在顶点着色器中使用,`attribute`变量用于接收从CPU传递而来的顶点信息:
|
||||
```glsl
|
||||
attribute vec4 vPos; // 表示顶点位置信息
|
||||
attribute vec3 vNormal; // 顶点法线信息
|
||||
```
|
||||
> `attribute`与`uniform`的不同在于,`attribute`变量记录的是单个顶点的具体信息,而`uniform`变量记录的信息是全局共享的,与具体顶点无关。
|
||||
#### 函数与内置函数
|
||||
GLSL允许我们自定义函数,自定义函数可以在着色器内部使用:
|
||||
```glsl
|
||||
float myMix(float a, float b, float alpha){
|
||||
// 根据alpha提供的比例来混合a和b
|
||||
return a + (b - a) * alpha;
|
||||
}
|
||||
```
|
||||
我们还可以使用关键字`in` `out` `inout`,来修改函数参数的使用性质:
|
||||
```glsl
|
||||
void sum1(in float a, in float b, out float result){
|
||||
// a和b作为输入参数,result作为输出参数
|
||||
result = a + b;
|
||||
}
|
||||
void sum2(inout float a, in float b){
|
||||
// a同时作为输出和输出参数,b仅作为输入参数
|
||||
a = a + b;
|
||||
}
|
||||
```
|
||||
GLSL还内置了一些函数,可以方便我们的着色器开发:
|
||||
```glsl
|
||||
max与min: 取最大和最小值
|
||||
mix: 按照给定的alpha值来混合给定的两个值或向量
|
||||
normalize: 求向量的标准化
|
||||
length: 求向量长度
|
||||
step: 比较给定两个数的大小,返回1.0或者0.0
|
||||
dot:求向量点乘结果
|
||||
cross:求向量叉乘结果
|
||||
```
|
||||
其他的内置函数还有很多,开发者可以参考网络上的相关文档。
|
||||
### 着色器结构示例
|
||||
不同游戏由于实现方案不同,着色器的具体细节也不相同,在此仅提供一份glsl着色器的基本框架以供参考,具体到《我的世界》,开发者可以参考官方现有的材质进行熟悉。
|
||||
```glsl
|
||||
// 顶点着色器示例
|
||||
uniform mat4 MVP; // CPU设定的MVP矩阵
|
||||
attribute vec4 vPos; // 顶点属性 - 顶点坐标
|
||||
attribute vec4 vColor; // 顶点属性 - 顶点色
|
||||
|
||||
varying vec4 outColor; // varying变量 用于传递给片元着色器
|
||||
|
||||
void main(){
|
||||
outColor = vColor;
|
||||
gl_Position = MVP * vPos; // 将变换后的顶点坐标赋值给内置变量
|
||||
}
|
||||
```
|
||||
```glsl
|
||||
// 片元着色器示例
|
||||
varying vec4 outColor; // 接收来自顶点着色器的varying
|
||||
|
||||
void main(){
|
||||
gl_FragColor = vec4(outColor.rgb, 1.0); // 利用分量访问将outColor的rgb信息输出,透明度使用常量1.0
|
||||
}
|
||||
```
|
||||
## 学习建议
|
||||
本文仅负责简单的入门教学,开发者如果希望深入学习,可以考虑参考其他网络资料:
|
||||
+ [零基础如何学习计算机图形学](https://www.zhihu.com/question/41468803/answer/1040420856)
|
||||
+ [GAMES101-现代计算机图形学入门](https://www.bilibili.com/video/BV1X7411F744)
|
||||
|
||||
如果开发者希望进一步学习OpenGL,可以参考相关资料:
|
||||
+ [LearnOpenGL](https://learnopengl-cn.github.io/)
|
||||
+ [GLSL中文手册](https://github.com/wshxbqq/GLSL-Card)
|
||||
3
mcguide/16-美术/7-材质与着色器/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# 前言
|
||||
|
||||
本教程将向大家讲解游戏中常见的一些对象是使用什么材质进行渲染,该材质中又是声明了哪个shader;在不同的shader中,我们能够使用哪些uniform常量;另外也将详细介绍材质的配置方式。
|
||||
BIN
mcguide/16-美术/7-材质与着色器/images/blend_glass.png
Normal file
|
After Width: | Height: | Size: 224 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/blend_water.png
Normal file
|
After Width: | Height: | Size: 233 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/blinn1.gif
Normal file
|
After Width: | Height: | Size: 17 MiB |
BIN
mcguide/16-美术/7-材质与着色器/images/blinn2.gif
Normal file
|
After Width: | Height: | Size: 16 MiB |
BIN
mcguide/16-美术/7-材质与着色器/images/entity_static.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/fancy_switch.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/frag_normal.gif
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
mcguide/16-美术/7-材质与着色器/images/inversed_steve.png
Normal file
|
After Width: | Height: | Size: 211 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/material_head.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/material_list.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/materialsample1.png
Normal file
|
After Width: | Height: | Size: 387 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/materialsample2.png
Normal file
|
After Width: | Height: | Size: 470 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/materialsample3.jpg
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/multipass_drawline.png
Normal file
|
After Width: | Height: | Size: 261 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/old_tv.gif
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
mcguide/16-美术/7-材质与着色器/images/rasterization.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/scan.gif
Normal file
|
After Width: | Height: | Size: 74 MiB |
BIN
mcguide/16-美术/7-材质与着色器/images/shader_headfiles.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
mcguide/16-美术/7-材质与着色器/images/vertex_color.jpg
Normal file
|
After Width: | Height: | Size: 69 KiB |