文章目录
LearnGL - 学习笔记目录
前些篇:
- LearnGL - 11.1 - 实现简单的Gouraud-Phong光照模型
- LearnGL - 11.2 - 实现简单的Phong光照模型
- LearnGL - 11.3 - 实现简单的Blinn-Phong光照模型
- LearnGL - 11.4 - 实现简单的Flat BlinnPhong光照模型
- LearnGL - 13 - PointLight - 点光源
- LearnGL - 13.1 - SpotLight - 聚光灯
这些演示光照计算中,分别对 方向光、点光源、聚光灯 类型的光源实现
这篇:我们将实现给 多光源 ,演示在场景中将摆放好 三种光源 的多个光源的效果
本人才疏学浅,如有什么错误,望不吝指出。
C++ 应用层的代码改动和核心代码
修改前
已成为历史的单光源代码:
int Camera::render(std::vector<GameObject*>* render_vec_helper) {
std::vector<MeshRenderer*> renderers;
// 提取 renderers
std::vector<GameObject*>::iterator begin = render_vec_helper->begin();
register size_t count = render_vec_helper->size();
int ret = 0;
for (register size_t i = 0; i < count; i++) {
GameObject* go = *(begin + i);
if (!go->getActivedInherited()) continue;
MeshRenderer* renderer = go->getComp<MeshRenderer>();
if (renderer != NULL && renderer->getActivedInherited()) {
renderers.push_back(renderer);
}
}
// 排序 renderers
std::sort(renderers.begin(), renderers.end(), rendererCmp);
// 处理 renderers
count = renderers.size();
for (register size_t i = 0; i < count; i++) {
MeshRenderer* renderer = renderers[i];
Material* mat = renderer->getAttachMaterial();
// retrive lights
std::vector<Component*>* lights = Component::getComps<Light>();
if (lights != NULL) {
std::vector<Component*>::iterator begin = lights->begin();
register size_t count = lights->size();
// 先使用,再设置
renderer->getAttachMaterial()->getShader()->use();
for (size_t i = 0; i < count; i++) {
Light* light = (Light*)*(begin + i);
if (!light->getActivedInherited()) {
mat->getShader()->setVec4("LightPos", 0, 0, 0, 0);
mat->getShader()->setVec4("LightColor", 0, 0, 0, 0);
continue;
}
switch (light->getType())
{
// 方向光假设为平行光,就想太阳一样,超级远都一样照射着
// 所以不用判断
case LightType::Directional:
{
// light world direction
vec3 light_world_dir = -light->getDir();
mat->getShader()->setVec4("LightPos",
light_world_dir.x, light_world_dir.y, light_world_dir.z, 0);
//mat->getShader()->setVec4("LightPos",
// 0.0f,0.0f,1.0f, 0.0f);
// light color
mat->getShader()->setVec4("LightColor",
light->color.x, light->color.y, light->color.z, light->intensity);
break;
}
// TODO : 点光源 需要判断是否有作用交集,才设置
case LightType::Point:
{
// light world position
vec3 light_world_pos = light->getWPos();
mat->getShader()->setVec4("LightPos",
light_world_pos.x, light_world_pos.y, light_world_pos.z, 1);
mat->getShader()->setVec4("LightColor",
light->color.x, light->color.y, light->color.z, light->intensity);
mat->getShader()->setFloat("ATTEN_Kc", light->point_light_constantK);
mat->getShader()->setFloat("ATTEN_Kl", light->point_light_linearK);
mat->getShader()->setFloat("ATTEN_Kq", light->point_light_quadraticK);
mat->getShader()->setVec2("ATTEN_Range", light->point_or_spot_light_range,
(light->point_or_spot_light_range == 0.0f ? 0.0f : 1.0f / light->point_or_spot_light_range));
break;
}
// TODO : 聚光灯 需要判断是否有作用交集,才设置
case LightType::Spot:
{
// light world position
vec3 light_world_pos = light->getWPos();
mat->getShader()->setVec4("LightPos",
light_world_pos.x, light_world_pos.y, light_world_pos.z, 0.5f);
mat->getShader()->setVec4("LightColor",
light->color.x, light->color.y, light->color.z, light->intensity);
mat->getShader()->setFloat("ATTEN_Kc", light->point_light_constantK);
mat->getShader()->setFloat("ATTEN_Kl", light->point_light_linearK);
mat->getShader()->setFloat("ATTEN_Kq", light->point_light_quadraticK);
mat->getShader()->setVec2("ATTEN_Range", light->point_or_spot_light_range,
(light->point_or_spot_light_range == 0.0f ? 0.0f : 1.0f / light->point_or_spot_light_range));
vec3 light_world_dir = light->getDir();
mat->getShader()->setVec3("SpotLightDir",
light_world_dir.x, light_world_dir.y, light_world_dir.z);
mat->getShader()->setFloat("SpotLightFOL", D2R(light->spot_light_FOL * 0.5f));
if (light->fade_out_using_FOL_percent) {
mat->getShader()->setFloat("SpotLightFOL_FadeOut", D2R((light->spot_light_FOL * light->fade_out_FOL_percent) * 0.5f));
}
else {
mat->getShader()->setFloat("SpotLightFOL_FadeOut", D2R(light->spot_light_FOL_fade_out * 0.5f));
}
mat->getShader()->setFloat("SpotLightExp", light->spot_light_exp);
break;
}
default:
std::cerr << "unimplements light type : " << (int)light->getType() << "\n";
assert(false);
break;
}
}
}
// camera pos
vec3 wp = trans->get_world_pos();
//vec3 wp = trans->local_position;
mat->getShader()->setVec3("_CamWorldPos", wp.x, wp.y, wp.z);
// projection matrix
Camera::mesh_renderer_data_helper.proj = getProjectionMatrix();
// view matrix
Camera::mesh_renderer_data_helper.view = getViewMatrix();
// camera pos
Camera::mesh_renderer_data_helper.camWorldPos = wp;
// render
renderer->render(Camera::mesh_renderer_data_helper);
ret++;
}
return ret;
}
修改后
int Camera::render(std::vector<GameObject*>* render_gos) {
glClearColor(clearColor.x, clearColor.y, clearColor.z, clearColor.w); // 设置清理颜色缓存时,填充的颜色值
glClearDepthf(clearDepth); // 设置清理深度缓存时,填充的深度值
glClear(clearFlag); // 清理颜色缓存 与 深度缓存
std::vector<MeshRenderer*> renderers;
// 提取 renderers
std::vector<GameObject*>::iterator begin = render_gos->begin();
register size_t count = render_gos->size();
int ret = 0;
for (register size_t i = 0; i < count; i++) {
GameObject* go = *(begin + i);
if (!go->getActivedInherited()) continue;
MeshRenderer* renderer = go->getComp<MeshRenderer>();
if (renderer != NULL && renderer->getActivedInherited()) {
renderers.push_back(renderer);
}
}
// 排序 renderers
std::sort(renderers.begin(), renderers.end(), rendererCmp);
// uniform 的名字缓存,128个字节应该够用了
char uniform_name[128];
// 灯光属性的 uniform 字符格式
const char* uniform_format = "Lights[%d].%s";
// 处理 renderers
count = renderers.size();
for (register size_t i = 0; i < count; i++) {
MeshRenderer* renderer = renderers[i];
Material* mat = renderer->getAttachMaterial();
// retrive lights
std::vector<Component*>* lights = Component::getComps<Light>();
if (lights != NULL) {
std::vector<Component*>::iterator begin = lights->begin();
register size_t count = lights->size();
// 先使用,再设置
renderer->getAttachMaterial()->getShader()->use();
size_t validatedLightCount = 0;
for (size_t i = 0; i < count; i++) {
Light* light = (Light*)*(begin + i);
// 没有启用
if (!light->getActivedInherited()) {
continue;
}
switch (light->getType())
{
// 方向光假设为平行光,就想太阳一样,超级远都一样照射着
// 所以不用判断
case LightType::Directional: {
break;
}
case LightType::Point: {
// TODO : 点光源 需要判断是否有作用交集,才设置
break;
}
case LightType::Spot:
{
// TODO : 聚光灯 需要判断是否有作用交集,才设置
// 张角
sprintf_s(uniform_name, uniform_format, validatedLightCount, "SpotFOL");
mat->getShader()->setFloat(uniform_name, D2R(light->spot_light_FOL * 0.5f));
if (light->fade_out_using_FOL_percent) {
// 淡出张角角度
sprintf_s(uniform_name, uniform_format, validatedLightCount, "SpotFOL_FadeOut");
mat->getShader()->setFloat(uniform_name, D2R((light->spot_light_FOL * light->fade_out_FOL_percent) * 0.5f));
}
else {
// 淡出张角角度
sprintf_s(uniform_name, uniform_format, validatedLightCount, "SpotFOL_FadeOut");
mat->getShader()->setFloat(uniform_name, D2R(light->spot_light_FOL_fade_out * 0.5f));
}
break;
}
default:
std::cerr << "unimplements light type : " << (int)light->getType() << "\n";
assert(false);
break;
}
if (light->getType() == LightType::Directional || light->getType() == LightType::Spot) {
// 方向
vec3 light_world_dir = -light->getDir();
sprintf_s(uniform_name, uniform_format, validatedLightCount, "Direction");
mat->getShader()->setVec3(uniform_name, light_world_dir.x, light_world_dir.y, light_world_dir.z);
}
if (light->getType() == LightType::Point || light->getType() == LightType::Spot) {
// 坐标
vec3 light_world_pos = light->getWPos();
sprintf_s(uniform_name, uniform_format, validatedLightCount, "Position");
mat->getShader()->setVec3(uniform_name, light_world_pos.x, light_world_pos.y, light_world_pos.z);
// 常数项系数
sprintf_s(uniform_name, uniform_format, validatedLightCount, "ATTEN_Kc");
mat->getShader()->setFloat(uniform_name, light->point_light_constantK);
// 一次项系数
sprintf_s(uniform_name, uniform_format, validatedLightCount, "ATTEN_Kl");
mat->getShader()->setFloat(uniform_name, light->point_light_linearK);
// 二次项系数
sprintf_s(uniform_name, uniform_format, validatedLightCount, "ATTEN_Kq");
mat->getShader()->setFloat(uniform_name, light->point_light_quadraticK);
// 范围
sprintf_s(uniform_name, uniform_format, validatedLightCount, "ATTEN_Range");
mat->getShader()->setVec2(uniform_name, light->point_or_spot_light_range,
(light->point_or_spot_light_range == 0.0f ? 0.0f : 1.0f / light->point_or_spot_light_range));
}
// 颜色
sprintf_s(uniform_name, uniform_format, validatedLightCount, "Color");
mat->getShader()->setVec3(uniform_name, light->color.x, light->color.y, light->color.z);
// 强度
sprintf_s(uniform_name, uniform_format, validatedLightCount, "Intensity");
mat->getShader()->setFloat(uniform_name, light->intensity);
// 类型
sprintf_s(uniform_name, uniform_format, validatedLightCount, "Type");
mat->getShader()->setInt(uniform_name, (GLint)light->getType());
// 启用
sprintf_s(uniform_name, uniform_format, validatedLightCount, "Enabled");
mat->getShader()->setInt(uniform_name, 1);
validatedLightCount++;
}
// 如果没有满灯光数量,则给最后一个索引的设置没启用,这样 shader 判断后面灯光的都不用处理
if (validatedLightCount < 10) {
sprintf_s(uniform_name, uniform_format, validatedLightCount, "Enabled");
mat->getShader()->setInt(uniform_name, 0);
}
}
// camera pos
vec3 wp = trans->get_world_pos();
//vec3 wp = trans->local_position;
mat->getShader()->setVec3("_CamWorldPos", wp.x, wp.y, wp.z);
// projection matrix
Camera::mesh_renderer_data_helper.proj = getProjectionMatrix();
// view matrix
Camera::mesh_renderer_data_helper.view = getViewMatrix();
// camera pos
Camera::mesh_renderer_data_helper.camWorldPos = wp;
// render
renderer->render(Camera::mesh_renderer_data_helper);
ret++;
}
return ret;
}
可以看到前后改动的地方就只是原来的单光源属性的 uniform 设置变成了光源数组中的 uniform 的设置
另外,我们可以看到有两个 TODO:
- // TODO : 点光源 需要判断是否有作用交集,才设置
- // TODO : 聚光灯 需要判断是否有作用交集,才设置
后续可以将其改为先检测光源的范围 BB 与渲染几何体的 BB (其中,BB的意思:Bounding Box包围盒)有交集后才传入该对象 shader 中来处理。
(目前我们的 Material 材质是 单个 shader 的,后续会再修改为:多个 pass ,类似 Unity 中的 ShaderLab 中的 Pass的作用)
Shader - my_lighting.glsl 核心改动
改动前
可以参考 LearnGL - 13.1 - SpotLight - 聚光灯 文章的完整 shader
改动后
// jave.lin - my_lighting.glsl - 光照模型处理
#include "/Include/my_global.glsl"
#ifndef _MY_LIGHTING__GLSL__
#define _MY_LIGHTING__GLSL__
#define DIRECTIONAL_LIGHT_TYPE 0
#define POINT_LIGHT_TYPE 1
#define SPOT_LIGHT_TYPE 2
// scene uniform
uniform vec4 _Ambient; // .xyz 环境光颜色, .w 环境光系数
uniform int AmbientType; // 环境光类别,[测试用]
// 材质属性 - 很多的属性可以合并到其他没有满4个分量的属性,但学习目的,为了可读性高,可以分开来
struct Material_t {
float Glossy; // 光滑度
vec3 Emission; // 自发光颜色
vec3 DiffuseK; // 漫反射系数
vec3 SpecularK; // 高光系数
};
uniform Material_t Material;
// 灯光属性 - 很多的属性可以合并到其他没有满4个分量的属性,但学习目的,为了可读性高,可以分开来
// 灯光属性也可以按类型来分为不同的材质结构体
// 但是为了编写方便,就没有分开了
struct LightData_t {
// common properties
int Enabled; // 是否开启
int Type; // 灯光类似
vec3 Color; // 灯光颜色
float Intensity; // 强度
// directional or spot light
vec3 Direction; // 方向光 或 聚光灯 照射方向
// point or spot light
vec3 Position; // 点光源 或 聚光灯 灯光世界坐标位置
float ATTEN_Kc; // 点光源 常数项系数
float ATTEN_Kl; // 点光源 一次项系数
float ATTEN_Kq; // 点光源 二次项系数
vec2 ATTEN_Range; // 点光源 有效范围, .x == range, .y == 1.0 / range
// spot light
float SpotFOL; // 聚光灯的张角量,Field Of Light(弧度)
float SpotFOL_FadeOut;// 这个边缘淡出的角度,必须小于 SpotFOL,否则没有效果
};
const uint MaxLightNum = 10;
uniform LightData_t Lights[MaxLightNum];
// ambient
vec3 getAmbient(vec3 albedo) {
if (AmbientType == 0) return _Ambient.rgb * _Ambient.a;
else return mix(_Ambient.rgb * _Ambient.a, albedo, _Ambient.a);
}
// point or spot light 的光距离衰减
float getDistanceAtten(float dist, LightData_t light) { // 获取距离衰减
// 由原来的 atten = 1 / (kc + kl * dist + kq * dist * dist) 改为只用一个 sommthstep就够了
// smoothstep 的曲线取反,即:1 - smoothstep,结果的曲线也不错
return 1 - smoothstep(0, light.ATTEN_Range.x, dist);
}
void phong_illumination(
in vec3 worldNormal,
in vec3 viewDir,
in vec3 worldPos,
out vec3 diffuse,
out vec3 specular
) {
for (int i = 0; i < MaxLightNum; ++i) {
LightData_t light = Lights[i];
// 如何有一个没开启,则后面都没开启,这是需要应用层配合使用的规则
if (light.Enabled == 0) break;
// 光源的方向
vec3 lightDir;
// 衰减
float atten = 1;
// 方向光
if (light.Type == DIRECTIONAL_LIGHT_TYPE) {
lightDir = light.Direction;
}
// 点光源
else {
// 点光源 或是 聚光灯 都需要处理的
lightDir = light.Position - worldPos; // 片段到光源的方向,简称:灯光方向
float dist = length(lightDir); // 片段到光源的距离
lightDir *= dist == 0 ? 1 : 1.0 / dist; // 归一化
atten = getDistanceAtten(dist, light); // 获取距离衰减
// 聚光灯
if (light.Type == SPOT_LIGHT_TYPE) {
float LdotSD = dot(lightDir, light.Direction);
float angle = acos(LdotSD);
if (angle < light.SpotFOL) {
// 在 FOL 张角内
if (angle > light.SpotFOL_FadeOut) {
// 平滑边缘
atten *= 1 - smoothstep(light.SpotFOL_FadeOut, light.SpotFOL, angle);
}
} else {
// 不在 FOL 张角内
atten = 0;
}
}
}
float D = max(0, dot(lightDir, worldNormal));
diffuse += light.Color * light.Intensity * D * Material.DiffuseK * atten;
vec3 H = normalize(lightDir + viewDir);
float S = 0;
if (D > 0) S = pow(max(0, dot(H, worldNormal)), Material.Glossy);
specular += light.Color * light.Intensity * S * Material.SpecularK * atten;
}
}
#endif
我这里的 多光源 的模型也比较的简单,就是之前的 Phong 模型的变种:
I
=
I
a
⋅
K
a
+
∑
i
=
0
n
(
I
d
⋅
K
d
⋅
d
o
t
(
N
,
L
i
)
+
I
s
⋅
K
s
⋅
p
o
w
(
d
o
t
(
n
o
r
m
a
l
i
z
e
(
L
i
+
V
)
,
N
)
,
G
l
o
s
s
y
)
)
I=\green{I_a\cdot K_a}+\red{\sum_{i=0}^n} \blue{(}I_d\cdot K_d\cdot dot(N,L_i) + I_s\cdot K_s\cdot pow(dot(normalize(L_i + V),N),Glossy)\blue{)}
I=Ia⋅Ka+∑i=0n(Id⋅Kd⋅dot(N,Li)+Is⋅Ks⋅pow(dot(normalize(Li+V),N),Glossy))
留意到前面的 环境光 I a ⋅ K a \green{I_a\cdot K_a} Ia⋅Ka 不参数叠加,之后后面的 漫反射 与 高光 都参与了叠加,使用了一个累加和: ∑ i = 0 n \red{\sum_{i=0}^n} ∑i=0n
注意: 在 for 循环里我们做了一些优化
// 如何有一个没开启,则后面都没开启,这是需要应用层配合使用的规则
if (light.Enabled == 0) break;
对应着上面 C++ 层改动后的:
// 如果没有满灯光数量,则给最后一个索引的设置没启用,这样 shader 判断后面灯光的都不用处理
if(validatedLightCount < 10) {
sprintf_s(uniform_name, light_format, validatedLightCount, "Enabled");
mat->getShader()->setInt(uniform_name, 0);
}
效果
放置多个光源
- 一个 方向光
- 两个 点光源
- 两个 聚光灯
C++ 应用接口就相当简单了,接口的设计参考了 Unity 的接口(我没有 Unity 源码,只是自己更具使用 Unity 习惯来设计)
目前没去制作 场景 的 导出、导入,先将就用用:
// load scene
SceneMgr::inst()->loadScene(new Scene());
mainCamera->addComp(new Camera());
mainCamera->getComp<Transform>()->local_position = vec3(0.0f, 2.0f, 10.0f);
mainCamera->getTrans()->set_world_pos(vec3(-10.0f, 2.0f, 12.0f));
mainCamera->getTrans()->set_world_rot(vec3(0.0, -21.0f, 0.0f));
GameObject* camParentGO = new GameObject("Cam parent GO");
camParentGO->add(mainCamera);
SceneMgr::inst()->getRoot()->add(camParentGO);
char name[128];
// 一个 方向光
{
GameObject* lightGO = new GameObject();
sprintf_s(name, "%s##%d", "DirectionalLight", lightGO->id());
lightGO->set_name_copy_from(name);
Light* lightComp = new Light();
lightGO->addComp(lightComp);
lightComp->setType(LightType::Directional);
lightGO->addComp(new MyLightVisual());
lightGO->getTrans()->set_world_pos(vec3(0.0f, 1.0f, 0.0f));
SceneMgr::inst()->getRoot()->add(lightGO);
}
// 两个 点光源
{
vec3 offset[2] = {
vec3(0, 2.0f, 2.0f),
vec3(0, 2.0f, 4.0f)
};
vec3 color[2] = {
vec3(1.0f, 0.0f, 0.0f),
vec3(0.0f, 1.0f, 0.0f),
};
for (size_t i = 0; i < 2; i++) {
GameObject* lightGO = new GameObject();
sprintf_s(name, "%s##%d", "PointLight", lightGO->id());
lightGO->set_name_copy_from(name);
Light* lightComp = new Light();
lightGO->addComp(lightComp);
lightComp->setType(LightType::Point);
lightComp->color = vec4(color[i], 0.5f);
lightGO->addComp(new MyLightVisual());
lightGO->getTrans()->set_world_pos(offset[i]);
SceneMgr::inst()->getRoot()->add(lightGO);
}
}
// 两个 聚光灯
{
vec3 offset[2] = {
vec3(-2.0f, 0.0f, 0.0f),
vec3(-2.0f, 0.0f, 2.0f)
};
vec3 rotate[2] = {
vec3(0.0, -90.0f, 0.0f),
vec3(0.0, -60.0f, 0.0f),
};
vec3 color[2] = {
vec3(1.0f, 0.0f, 1.0f),
vec3(1.0f, 1.0f, 0.0f),
};
for (size_t i = 0; i < 2; i++) {
GameObject* lightGO = new GameObject();
sprintf_s(name, "%s##%d", "SpotLight", lightGO->id());
lightGO->set_name_copy_from(name);
Light* lightComp = new Light();
lightGO->addComp(lightComp);
lightComp->setType(LightType::Spot);
lightComp->color = vec4(color[i], 0.5f);
lightGO->addComp(new MyLightVisual());
lightGO->getTrans()->set_world_pos(offset[i]);
lightGO->getTrans()->set_world_rot(rotate[i]);
SceneMgr::inst()->getRoot()->add(lightGO);
}
}
// geometry
{
for (size_t i = 0; i < 10; i++) {
// in fs
{
// sphere
GameObject* temp_go = new GameObject();
sprintf_s(name, "%s##%d", "sphere", temp_go->id());
temp_go->set_name_copy_from(name);
temp_go->getTrans()->local_position = vec3(0, 0, i);
MySphere_Testing_Includes* temp_comp = new MySphere_Testing_Includes();
temp_go->addComp(temp_comp);
SceneMgr::inst()->getRoot()->add(temp_go);
}
{
// balloon cat
GameObject* temp_go = new GameObject();
sprintf_s(name, "%s##%d", "Balloon cat", temp_go->id());
temp_go->set_name_copy_from(name);
temp_go->getTrans()->local_position = vec3(1.2f, 0, i);
temp_go->getTrans()->local_scale = vec3(3, 3, 3);
MyBalloonCat_Testing_Includes* temp_comp = new MyBalloonCat_Testing_Includes();
temp_go->addComp(temp_comp);
SceneMgr::inst()->getRoot()->add(temp_go);
}
}
} // end blinn phong
效果
咋一看,眼花缭乱!
添加了灯光 Gizmos 显示控制
所以我们添加了一个 ImGui 的编辑选项,让选中的光源对象才显示:
// 灯光 Gizmos 显示方式
enum class LightVisualType {
ShowSelected, // 显示选中
ShowAll, // 实现所有
HideAll, // 隐藏所有
MAX_NUM // 枚举最大数量
};
struct LightVisual {
public:
static const char* name[(size_t)LightVisualType::MAX_NUM];
static LightVisualType value[(size_t)LightVisualType::MAX_NUM];
};
const char* LightVisual::name[(size_t)LightVisualType::MAX_NUM] = {
"ShowSelected",
"ShowAll",
"HideAll",
};
LightVisualType LightVisual::value[(size_t)LightVisualType::MAX_NUM] = {
LightVisualType::ShowSelected, // 显示选中
LightVisualType::ShowAll, // 实现所有
LightVisualType::HideAll, // 隐藏所有
};
...
void MyLightVisual::update() {
Component::update();
...
switch (MyLightVisual::visualType) {
case LightVisualType::ShowSelected: {
renderer->setActived(getOwner()->id() == Editing_Info::hierarchy_info.obj_id);
break;
}
case LightVisualType::ShowAll: {
renderer->setActived(true);
break;
}
case LightVisualType::HideAll: {
renderer->setActived(false);
break;
}
default:
break;
}
}
void MyLightVisual::imgui_content() {
Component::imgui_content();
ImGui::Combo("Light Gizmos ShowType", (int*)&MyLightVisual::visualType, LightVisual::name, (int)LightVisualType::MAX_NUM);
}
对应 GIF:
这也是临时的方案,后续会封装 Gizmos,因为,正确的处理是会对所有 GameObject 的 Gizmos 的同样处理。
添加了渲染队列控制
因为我们的灯光可视 Gizmos 是半透明的,所以我们也封装了一些临时处理的:渲染队列
设置方式可以是数值,也可以是枚举值,枚举值参考了 Unity 的数值:
enum class RenderQueueType {
Background = 1000, // 天空盒子
Gemoetry = 2000, // 不透明几何体
AlphaTest = 2500, // FS 带有 discard 的
Transprent = 3000, // 透明的几何体
Gizmos = 3500, // 开发调式用的 Gizmos 绘制
Overlay = 4000, // UI
};
如果不处理渲染队列问题,我们半透明对象可能会出现融混(Blending)效果不对的问题,所以才添加了这些功能。
渲染状态
目前只有小部分:线框模式、面向剔除、深度测试、深度写入、融混
上面说明半透明是上图的:Blend 选项部分的内容,可以设置 Factor 与 Equation
以后还会不全所有的参数设置:如:glPolygonOffset
(类似Unity ShaderLab Pass 中的 Offset[u] [m]
)、模板测试、等等,以后用到再添加。
所有这些渲染状态配置类的,后面会添加一章单独简述一篇。这里因为有些功能需要,而提前用到,顺便在此说明一下。
整体操作演示
References
- 多光源 - 它里面的光照模型和我的不太一样,我的是自己在 Phong 模型上总结的变种经验模型。