LearnGL - 13 - PointLight - 点光源

49 篇文章 0 订阅
47 篇文章 0 订阅


LearnGL - 学习笔记目录

前些篇:

这些演示光照计算中,都是对 “方向光” 类型的光源来实现的

这篇:我们尝试给 “点光源” 类型的光源

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


先看一下我们的效果静态图:
在这里插入图片描述


点光源


与方向光比较

  • 方向光 : 只 考虑方向不考虑位置,而且他的光强度假设是不衰减
  • 点光源 : 刚好相反,不考虑方向,但需要 考虑光源位置,而且它的光强度会随距离而衰减

光衰减


距离衰减

目前参考了网上提供的衰减曲线,因为直线型的衰减效果不太好
在这里插入图片描述
Y轴数值我放大了100倍,因为实际 shader 中使用是 0~1 的一致衰减值

衰减公式为:
a t t e n d ( d ) = 1 K c + K l ⋅ d + K q ⋅ d 2 atten_d(d)=\frac{1}{K_c+K_l \cdot d + K_q \cdot d^2} attend(d)=Kc+Kld+Kqd21

  • d d d 是距离
  • K c K_c Kc 是常数项系数,通常为1,为了避免除以零
  • K l K_l Kl 是一次项系数
  • K q K_q Kq 是二次项系数

作用范围衰减

但是因为我的点光源为了优化,添加了一个 range 参数,即:点光源的影响范围,所以我给上面的衰减模型公式添加了另一个衰减值:atten_r r r r 是光源指定的有效的范围值
a t t e n r ( r ) = 1 − 1 r , ( 0 ≤ r ≤ 指 定 的 范 围 值 ) atten_r(r)=1-\frac{1}{r}, (0 \le r \le 指定的范围值) attenr(r)=1r1,(0r)

所以我的 a t t e n ( x ) atten(x) atten(x) 调整为:
a t t e n ( d , r ) = a t t e n d ( d ) ⋅ a t t e n r ( r ) atten(d,r)=atten_d(d) \cdot atten_r(r) atten(d,r)=attend(d)attenr(r)

用 GGB 可以调整系数查看效果:
在这里插入图片描述
在这里插入图片描述


对应的 Inspector 面板参数

在这里插入图片描述
首先是需要选择为 Point 点光源

  • Atten Kc 对应的常数项系数
  • Atten Kl 一次项系数
  • Atten Kq 二次项系数
  • Range 就是我们指定该点光源能作用的范围大小

演示

为了方便查看点光源的效果,先将镜头的 clearColor 设置为纯黑色
在这里插入图片描述

再关闭环境光: AmbientIntensity = 0
在这里插入图片描述
调整 Range 参数查看效果,并调整一下灯光颜色
在这里插入图片描述

光源的切换

我给编辑器中的光源作了类似 Gizmos 的显示切换

  • 方向光 目前显示的是一个 Cube,从黑色指定有颜色的方向就是光源入射方向
  • 点光源 目前实现的是一个 Sphere,不用考虑方向,但需要考虑位置

在这里插入图片描述


Shader

主要是查看:my_lighting.glsl 的引用文件(GLSL 中如何实现 include 可以查看我之前的一篇:LearnGL - 12 - GLSL include - GL_ARB_shading_language_include (Extensions扩展) - 各种踩坑

// jave.lin - my_lighting.glsl - 光照模型处理

#include "/Include/my_global.glsl"

#ifndef _MY_LIGHTING__GLSL__
#define _MY_LIGHTING__GLSL__

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

// object uniform
uniform float Glossy;		// 光滑度
uniform vec3 DiffuseK;		// 漫反射系数
uniform vec3 SpecularK;		// 高光系数

// light uniform
uniform vec4 LightPos;		// 灯光世界坐标位置,w==0,或名是方向光,w==1说明是点光源,w == 0.5 是聚光灯
uniform vec4 LightColor;	// 灯光颜色,.xyz 顔色,.w 强度
// uniform vec3 LightDir;		// 灯光类型为聚光灯的方向

// point light
uniform float PL_Kc;		// 点光源 常数项系数
uniform float PL_Kl;		// 点光源 一次项系数
uniform float PL_Kq;		// 点光源 二次项系数
uniform vec2 PL_Range;		// 点光源 有效范围, .x == range, .y == 1.0 / range

// TODO : 实现多光源时使用,现在单光源先不写
// struct LightData_t {
// };

// 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 light
float getDistanceAtten(float dist) {		// 获取距离衰减
	return 1.0 / (PL_Kc + PL_Kl * dist + PL_Kq * (dist * dist));
}
float getRangeAtten(float dist) {			// 获取范围衰减
	return clamp(dist * PL_Range.y == 0 ? 0 : 1 - dist * PL_Range.y, 0, 1);
}

void phong_illumination(
    in vec3 worldNormal,
    in vec3 viewDir,
    in vec3 worldPos,
    out vec3 diffuse,
    out vec3 specular
    ) {

	vec3 lightDir;
	float atten = 1;
    if (LightPos.w == 0) {
		// 下面使用的是Phong 光照模型
		// 如果是方向光,那么 LightPos.xyz 是灯光方向的反方向
		lightDir = LightPos.xyz;
		
	} else {
		// 点光源 或是 聚光灯
		if (LightPos.w == 1) {
			// 点光
			lightDir = LightPos.xyz - worldPos;
			float dist = length(lightDir);
			lightDir *= dist == 0 ? 1 : 1.0 / dist;
			// lightDir = normalize(lightDir);
			atten = getDistanceAtten(dist) * getRangeAtten(dist);
		} else { // LightPos.w == 0.5,即:LightPos.w !=0 && LightPos.w != 1
			// 聚光灯
		}
	}
	float D = max(0, dot(lightDir, worldNormal));
	diffuse = LightColor.rgb * LightColor.a * D * DiffuseK * atten;
	vec3 H = normalize(lightDir + viewDir);
	float S = 0;
	if (D > 0) S = pow(max(0, dot(H, worldNormal)), Glossy);
	specular = LightColor.rgb * LightColor.a * S * SpecularK * atten;
}

#endif

my_global.glsl

// jave.lin - my_global.glsl

#ifndef _MY_GLOBAL__GLSL__
#define _MY_GLOBAL__GLSL__

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

// 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__ */ // 这里一定要加 /* 你的头文件宏 */,否则会报错,太无语了

接着是使用渲染对象的 shader

// jave.lin - testing_includes.vert
#version 450 compatibility
#extension GL_ARB_shading_language_include : require
#include "/Include/my_global.glsl"

// vertex data
in vec3 vPos;		// 顶点坐标
in vec2 vUV0;		// 顶点纹理坐标
in vec3 vNormal;		// 顶点法线

// vertex data - interpolation
out vec2 fUV0;			// 给 fragment shader 传入的插值
out vec3 fNormal;		// 世界坐标顶点法线
out vec3 fWorldPos;		// 世界坐标

void main() {
	vec4 worldPos = mMat * vec4(vPos, 1.0);	// 世界坐标
	fUV0 = vUV0;							// UV0
	fNormal = ObjectToWorldNormal(vNormal);	// 世界坐标顶点法线
	fWorldPos = worldPos.xyz;				// 世界坐标
	gl_Position = pMat * vMat * worldPos;	// Clip pos
}

// jave.lin - testing_includes.frag
#version 450 compatibility
#extension GL_ARB_shading_language_include : require
#include "/Include/my_global.glsl"
#include "/Include/my_lighting.glsl"

// interpolation - 插值数据
in vec2 fUV0;			// uv 坐标
in vec3 fNormal;		// 顶点法线
in vec3 fWorldPos;		// 世界坐标

uniform sampler2D main_tex;

void main() {

	vec3 albedo 	= texture(main_tex, fUV0).rgb;

	vec3 worldNormal= normalize(fNormal);						// 世界坐标法线再次归一化一次,因为插值之后可能会导致不是归一化的值
	vec3 viewDir 	= getWorldViewDir(fWorldPos); 	    		// 顶点坐标 指向 镜头坐标 的方向

	vec3 diffuse 	= vec3(0);
	vec3 specular	= vec3(0);

    vec3 ambient 	= getAmbient(albedo);

	phong_illumination(worldNormal, viewDir, fWorldPos, diffuse, specular);

	gl_FragColor = vec4(ambient + diffuse * albedo + specular, 1.0);
}

References

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值