LearnGL - 15.1 - Reflection - 反射效果

文章目录

  • 先看看效果
  • 思路
  • 构造 cube map、纯颜色环境光
  • 计算反射向量
  • 添加对环境反射的强度 ReflectionK
    • 完整的 my_global.glsl
    • 完整的 my_lighting.glsl
  • References


LearnGL - 学习笔记目录

前一篇:LearnGL - 15 - Skybox - 天空盒 - 了解了如何构造一个天空盒

这一篇:实现类似环境反射的效果,我们可以使用纯颜色、天空盒来做反射采样的颜色。

总体来说我现实的这种比较简单,不过很多都是基于之前的内容类实现的。

本人才疏学浅,如有什么错误,望不吝指出。


先看看效果

LearnGL - 15.1 - Reflection - 反射效果_第1张图片


思路

  • 构造好用于环境反射用的 cube map,或是纯颜色的 环境颜色,并传入到 shader
  • 计算反射向量,用于采样 cube_map,或是纯颜色
  • 添加对环境反射的强度 ReflectionK

构造 cube map、纯颜色环境光

  • 构造 cube map:可参考前一篇:LearnGL - 15 - Skybox - 天空盒
  • 纯颜色环境光,也可以参考之前我们的: LearnGL - 11.1 - 实现简单的Gouraud光照模型 本身就有了,可以直接拿来用即可

计算反射向量

用到 r e f l e c t ( I , N ) reflect(I, N) reflect(I,N),也可以参考会之前的 LearnGL - 11.1 - 实现简单的Gouraud光照模型 - reflect - 反射高光方向

LearnGL - 15.1 - Reflection - 反射效果_第2张图片


添加对环境反射的强度 ReflectionK

这个具体看 shader 了

LearnGL - 15.1 - Reflection - 反射效果_第3张图片
LearnGL - 15.1 - Reflection - 反射效果_第4张图片

主要看:calculateReflection 函数

void calculateReflection(vec3 worldNormal, vec3 viewDir, inout vec3 albedo) {
		if (Material.ReflectionK > 0) {
		vec3 reflection_color   = albedo;
		if (ClearType == CLEAR_TYPE_SKYBOX) {											// 天空盒的
			vec3 R = reflect(-viewDir, worldNormal);
			reflection_color 	= texture(SkyboxTex, R).rgb;
		} else if (ClearType == CLEAR_TYPE_COLOR) {										// 清理颜色的
			reflection_color 	= ClearColor;											// 不用计算反射
		}
		albedo             		= mix(albedo, reflection_color, Material.ReflectionK);
	}
}

完整的 my_global.glsl

// jave.lin - my_global.glsl

#ifndef _MY_GLOBAL__GLSL__
#define _MY_GLOBAL__GLSL__

#define CLEAR_TYPE_COLOR 0
#define CLEAR_TYPE_SKYBOX 1

// camera uniform
uniform vec3 _CamWorldPos;		// 镜头世界坐标

uniform int ClearType;			// 渲染相机的 clear type
uniform vec3 ClearColor;		// 清理的颜色
uniform samplerCube SkyboxTex; 	// 天空盒

// scene uniform
uniform vec4 _Ambient;		// .xyz 环境光颜色, .w 环境光系数
uniform int AmbientType;	// 环境光类别,[测试用]

// local uniform
uniform mat4 mMat; 				// m 矩阵
uniform mat4 vMat; 				// v 矩阵
uniform mat4 pMat; 				// p 矩阵
uniform mat4 mvpMat; 			// m.v.p 矩阵
uniform mat4 IT_mMat;			// Model Matrix 的逆矩阵的转置矩阵

// 将对象空间的法线转换到世界空间下的法线
vec3 ObjectToWorldNormal(vec3 n) {
	return normalize(mat3(IT_mMat) * n);	// 等价于:transpose(I_mMat) * vec4(n, 0)
}

vec3 getWorldViewDir(vec3 worldPos) {
	return normalize(_CamWorldPos - worldPos);
}

#endif /* _MY_GLOBAL__GLSL__ */ // 这里一定要加 /* 你的头文件宏 */,否则会报错,太无语了

完整的 my_lighting.glsl

// 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

// 材质属性 - 很多的属性可以合并到其他没有满4个分量的属性,但学习目的,为了可读性高,可以分开来
struct Material_t {
	float Glossy;			// 光滑度
	vec3 Emission;			// 自发光颜色
	vec3 DiffuseK;			// 漫反射系数
	vec3 SpecularK;			// 高光系数
	float ReflectionK;		// 反射系数
};
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;
	}
}

void calculateReflection(vec3 worldNormal, vec3 viewDir, inout vec3 albedo) {
		if (Material.ReflectionK > 0) {
		vec3 reflection_color   = albedo;
		if (ClearType == CLEAR_TYPE_SKYBOX) {											// 天空盒的
			vec3 R = reflect(-viewDir, worldNormal);
			reflection_color 	= texture(SkyboxTex, R).rgb;
		} else if (ClearType == CLEAR_TYPE_COLOR) {										// 清理颜色的
			reflection_color 	= ClearColor;											// 不用计算反射
		}
		albedo             		= mix(albedo, reflection_color, Material.ReflectionK);
	}
}

#endif

References

  • 立方体贴图

你可能感兴趣的:(OpenGL,图形)