<十五>入门CocosShader之改造精灵Shader
前言
在CocosEffect中内置有一个shader--builtin-sprite,它在基础绘图 shader 的基础上增加了纹理的处理。
Sprite Shader
在资源管理器中创建一个sprite.effect,拷贝 builtin-sprite 内所有的内容
// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
CCEffect %{
techniques:
- passes:
- vert: sprite-vs:vert
frag: sprite-fs:frag
depthStencilState:
depthTest: false
depthWrite: false
blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
blendDstAlpha: one_minus_src_alpha
rasterizerState:
cullMode: none
properties:
alphaThreshold: { value: 0.5 } # 定义属性透明度阈值,在启用透明测试的时候,透明度低于阈值内值的片段会被丢弃
# 这里对应的uniform是在sprite-fs的alpha-test中定义的
}%
CCProgram sprite-vs %{
precision highp float;
#include <builtin/uniforms/cc-global>
#if USE_LOCAL
#include <builtin/uniforms/cc-local>
#endif
#if SAMPLE_FROM_RT
#include <common/common-define>
#endif
in vec3 a_position;
in vec2 a_texCoord;
in vec4 a_color;
out vec4 color;
out vec2 uv0;
vec4 vert () {
vec4 pos = vec4(a_position, 1);
//因为2D大部分图像都是用来做UI的,UI的变化频率很大
//假设所有的UI都是动态的,因此需要每帧检查对象是否需要重新计算
//这时就会在CPU进行模型空间到世界空间到转换,但这种做法不一定适合所有的项目
//可能有些项目的UI比较固定变化不大,顶点数据直接提供模型空间即可,后续的坐标转换都在GPU上进行,可以减少CPU的开销
//因此提供了USE_LOCAL这样一个宏定义,让用户自己决定2D顶点数据的计算方式
#if USE_LOCAL
pos = cc_matWorld * pos;
#endif
//这里的宏是处理像素对齐的,比如像素风游戏每个像素的边缘都很是清晰分明的,在进行多次坐标转换时精度差值也会变大
//可能会出现像素抖动的现象
//这里的像素对齐就是对观察空间的像素做取整处理
//然后再让它转换到裁剪空间,可以有效避免像素抖动
#if USE_PIXEL_ALIGNMENT
pos = cc_matView * pos;
pos.xyz = floor(pos.xyz);
pos = cc_matProj * pos;
#else
pos = cc_matViewProj * pos;
#endif
uv0 = a_texCoord;
#if SAMPLE_FROM_RT
CC_HANDLE_RT_SAMPLE_FLIP(uv0);
#endif
color = a_color;
return pos;
}
}%
CCProgram sprite-fs %{
precision highp float;
#include <builtin/internal/embedded-alpha>
#include <builtin/internal/alpha-test>
in vec4 color;
#if USE_TEXTURE
in vec2 uv0;
//顶点属性的内存分配
#pragma builtin(local)//申请local内存
layout(set = 2, binding = 12) uniform sampler2D cc_spriteTexture;//layout代表绑定到local内存指定位置
#endif
vec4 frag () {
vec4 o = vec4(1, 1, 1, 1);
#if USE_TEXTURE
//o *= CCSampleWithAlphaSeparated(cc_spriteTexture, uv0);
o *= texture(cc_spriteTexture,uv0);//直接做纹理处理即可
#if IS_GRAY //引擎设置的灰度计算
float gray = 0.2126 * o.r + 0.7152 * o.g + 0.0722 * o.b;
o.r = o.g = o.b = gray;
#endif
#endif
o *= color;
ALPHA_TEST(o);//在启用alpha测试时,会调用,如果小于阈值会被丢弃,以达到该区域呈现透明的效果
return o;
}
}%
创建一个sprite材质并应用sprite.effect.
在场景中创建一个精灵并挂载sprite材质
可以看到精灵显示变成了纯白色,这是因为默认的宏是禁用状态,在材质属性中勾选启用纹理即可。
上述是在编辑器环境下对材质的赋值,很多情况下希望在运行时动态修改。这就需要在代码中去调用相应的API。
import { _decorator, Component, EffectAsset, Graphics, Material, Node, Sprite } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('Draw')
export class Draw extends Component {
@property(EffectAsset)
effect: EffectAsset = null;//声明一个属性去关联EffectAsset
start() {
// //获取基础绘制组件Graphics
// const g = this.getComponent(Graphics);
// //绘制并填充矩形
// g.fillRect(0, 0, 400, 300);
//new一个材质
const mat = new Material();
//初始化
mat.initialize({ effectAsset: this.effect ,defines: {USE_TEXTURE:true}});
//添加材质到sprite
const spComp = this.node.getComponent(Sprite);
spComp.customMaterial = mat;
}
}
补充
include 机制:
类似 C/C++ 的头文件 include 机制,可以在任意 shader 代码(CCProgram 块或独立的头文件)中引入其他代码片段:
// 内置头文件引入
#include <cc-global>
// 自定义头文件引入,采用的是:
// 1. 相对于项目目录 assets/chunks 文件夹位置(即使 assets 下没有 chunks 文件夹,也会假设有一个 chunks 文件夹,并相对于这个文件夹查找)
// 2. 相对于当前文件路径查找
// 前者优先。
// 更多有关绝对路径或同文件引用,请查看:https://docs.cocos.com/creator/3.2/manual/zh/material-system/effect-syntax.html#include-机制
#include "../headers/my-shading-algorithm.chunk"
Cocos Creator 所有的内置 chunk 都在“资源管理器” internal 下的 chunks 文件夹里,引用内置的 chunk,直接 "#include
预处理宏定义:
在任意 shader 代码(CCProgram 块或独立的头文件)中,都可以通过 大写字母 + _ 的方式定义宏。每一个宏都是一个开关,默认是关闭状态,可以在编辑器或者运行时开启,需要通过 if 语句进行判断。如果不是 GLSL 内置宏,请不要使用 ifdef 或者 if defined 做判断,否则始终为 true。
// USE_TEXTURE 就是一个宏定义,所有的宏定义最终都会被序列化到“属性检查器”面板上,方便随时启/禁用。
// 如果宏定义处于禁用中, #if 到 #endif 内的代码不会执行
#if USE_TEXTURE
in vec2 uv0;
#pragma builtin(local)
layout(set = 2, binding = 10) uniform sampler2D cc_spriteTexture;
#endif
// 也可以通过代码对宏进行启/禁用
const meshRenderer = this.getComponent(MeshRenderer);
const mat = meshRenderer.material;
mat.recompileShaders({ USE_TEXTURE: true });
Cocos Effect 所编写的 Shader 是无法单独使用的,必须要结合材质使用。材质是一个具备与光交互,供渲染器读取的数据集,可以为渲染器提供贴图纹理、光照算法等,可以直接将材质资源挂载到模型对象身上。材质可以持有多个 technique,但是在运行时有且仅能使用一个。材质包含的可配置参数如下:
effectAsset 或 effectName:effect 资源引用,指定使用哪个 EffectAsset 所描述的流程进行渲染。(必备)
technique:指定使用 EffectAsset 中的第几个 technique,默认为第 0 个。
defines:宏定义列表,指定开启哪些宏定义,默认全部关闭。
states:管线状态重载列表,指定对渲染管线状态(深度模板透明混合等)有哪些重载,默认与 effect 声明一致。
const mat = new Material();
mat.initialize({
effectName: 'pipeline/skybox',
defines: {
USE_RGBE_CUBEMAP: true
}
});
浙公网安备 33010602011771号