LearnGL - 15.2 - Refraction - 折射效果

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


LearnGL - 学习笔记目录

前些篇:

这一篇:实现类似折射的效果

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


扯一些闲话:

前几天和小舅子玩了玩 PS4:

  • 《Hell Driver》(地狱老司机,这个游戏数值做得太渣,想不通一个游戏数值制作这么差)
  • 《Alienation》(异化,和 我比较喜欢的《Dead Nation》同开发商的产品,操作手感很不错)
  • 《God of War III》(战神3,之前没空玩,然后花了两天通关中等难度,总体感觉战神3操作手感不是很好,战神4之前视频通关过,但也没时间去玩,战神4的剧情、渲染、操作手感,都有着非常大的提升、序幕也很期待与雷神索尔之战:God of War V,战神5)

现在回过头来看看这些以前很羡慕游戏开发,现在感觉渲染技术上来说越来越简单了,因为有了一些基础积累(注意是:基础)。

但是要制作这些大作,对场景、特效、动作/动画、音效、剧情、关卡、数值有着巨量工作,最后是调试也是巨量的、反复的。

战神3 我连最后所有的 谢幕信息都看完了,开发阵容真的强大,投入很大!


先看看效果

在这里插入图片描述

下面是反射+折射 各50%,再加上半透明 Blend SrcAlpha OneMinusSrcAlpha 的融混效果
在这里插入图片描述

目前这个效果是只对 Skybox 天空盒 作为环境贴图的反射、折射的效果,所以可以看到,气球猫 与 球体 之间没有反射 和 折射


折射现象

引用 百度百科:折射 一段话:

光的折射:光从一种透明介质斜射入另一种透明介质时,传播方向一般会发生变化,这种现象叫光的折射。理解:光的折射与光的反射一样都是发生在两种介质的交界处,只是反射光返回原介质中,而折射光则进入到另一种介质中,由于光在在两种不同的物质里传播速度不同,故在两种介质的交界处传播方向发生变化,这就是光的折射。注意:在两种介质的交界处,既发生折射,同时也发生反射。反射光光速与入射光相同 ,折射光光速与入射光不同。

引用 wiki 斯涅尔定律 的一段话:

当光波从一种介质传播到另一种具有不同折射率的介质时,会发生折射现象,其入射角与折射角之间的关系,可以用斯涅尔定律(Snell’s Law)来描述。斯涅尔定律是因荷兰物理学家威理博·斯涅尔而命名,又称为“折射定律”。
在光学里,光线跟踪科技应用斯涅尔定律来计算入射角与折射角。在实验光学与宝石学里,这定律被应用来计算物质的折射率。对于具有负折射率的负折射率超材料(metamaterial),这定律也成立,允许光波因负折射角而朝后折射。
斯涅尔定律表明,当光波从介质1传播到介质2时,假若两种介质的折射率不同,则会发生折射现像,其入射光和折射光都处于同一平面,称为“入射平面”,并且与界面法线的夹角满足如下关系:
n 1 sin ⁡ θ 1 = n 2 sin ⁡ θ 2 n 1 sin ⁡ θ 1 = n 2 sin ⁡ θ 2 {\displaystyle n_{1}\sin \theta _{1}=n_{2}\sin \theta _{2}}n_{1}\sin \theta _{1}=n_{2}\sin \theta _{2} n1sinθ1=n2sinθ2n1sinθ1=n2sinθ2
其中, n 1 n 1 、 n 2 n 2 {\displaystyle n_{1}}n_{1}、{\displaystyle n_{2}}n_{2} n1n1n2n2分别是两种介质的折射率, θ 1 θ 1 {\displaystyle \theta _{1}}\theta_1 θ1θ1 θ 2 θ 2 {\displaystyle \theta _{2}}\theta_2 θ2θ2分别是入射光、折射光与界面法线的夹角,分别叫做“入射角”、“折射角”。
这公式称为“斯涅尔公式”。
斯涅尔定律可以从费马原理推导出来,也可以从惠更斯原理、平移对称性或麦克斯韦方程组推导出来。

再引用 立方体贴图 里的一张图:
在这里插入图片描述


refract 函数的使用

参考 CG refract 函数 - nvidia 上的 cg refract 文档,说明不够详细,只是将函数的定义原型给出了

可以看出来:

float3 refract(float3 i, float3 n, float eta);

refract 的参数说明:

  • float3 i 是入射角
  • float3 n 是表面法线
  • float eta源物质折射率目标物质折射率 之比

前两个参数都很好理解,第三个参数需要了解 源物质折射率目标物质折射率 的区别

假设 空气折射率为:1.0水的折射率为 1.33 ,那么从 空气 中传入的 refract 的第三个参数为: 1.0 / 1.33,那么可得知 源物质折射率 为:空气目标物质折射率 为:

反推,如果从水底中观察水面上的物质时,refract 的第三个参数就应该传入:1.33/1.00水的折射率 / 空气折射率


上面的折射率都是科学家通过实验总结出来的规律值

常用物质折射率表

材质折射率
空气1.00
1.33
1.309
玻璃1.52
钻石2.42

这次我们的 shader 代码有调整:

Shader 调整

my_lighting.glsl

添加材质属性

// 材质属性 - 很多的属性可以合并到其他没有满4个分量的属性,但学习目的,为了可读性高,可以分开来
struct Material_t {
	float Glossy;			// 光滑度
	vec3 Emission;			// 自发光颜色
	vec3 DiffuseK;			// 漫反射系数
	vec3 SpecularK;			// 高光系数
	int UseTexAlpha;		// 是否使用纹理 alpha 值
	float Alpha;			// 当 UseTexAlpha == 0,则使用该值
	float ReflectionK;		// 反射系数,反射越强,折射越小,所以也会影响折射效果
	float RefractionK;		// 折射效果强度
	float SrcRefractionK;	// 源物质折射率
	float DstRefractionK;	// 目标物质折射率
};
uniform Material_t Material;

这一次主要添加了:

	int UseTexAlpha;		// 是否使用纹理 alpha 值
	float Alpha;			// 当 UseTexAlpha == 0,则使用该值
	float RefractionK;		// 折射效果强度
	float SrcRefractionK;	// 源物质折射率
	float DstRefractionK;	// 目标物质折射率

添加了着色器信息结构体

// Shading Info 着色信息
struct ShadingInput_t {
	vec4 tex_col;
	vec3 normal;
	vec3 worldPos;
};

添加了计算折射的函数

// 只计算折射
void calculate_refraction(vec3 worldNormal, vec3 viewDir, vec3 albedo, out vec3 refraction) {
	refraction   = albedo; // 环境 或 自身反射率 颜色
	if (ClearType == CLEAR_TYPE_SKYBOX) {											// 天空盒的
		vec3 R = refract(-viewDir, worldNormal, Material.SrcRefractionK / Material.DstRefractionK);  // 外部可以优化,传入一个 SrcRefraction / DstRefraction 的值,但这里学习用,写清晰一些
		refraction 	= texture(SkyboxTex, R).rgb;
	} else if (ClearType == CLEAR_TYPE_COLOR) {										// 清理颜色的
		refraction 	= ClearColor;													// 不用计算反射
	}
}

提取了着色函数

// 着色计算
void shading(ShadingInput_t input_info, out vec4 output_col) {
	vec3 albedo 	            = input_info.tex_col.rgb;
	float alpha					= Material.UseTexAlpha == 0 ? Material.Alpha : input_info.tex_col.a;
	vec3 worldNormal            = normalize(input_info.normal);							// 世界坐标法线再次归一化一次,因为插值之后可能会导致不是归一化的值
	vec3 viewDir 	            = getWorldViewDir(input_info.worldPos); 	  			// 顶点坐标 指向 镜头坐标 的方向

	vec3 reflection, refraction;
	calculate_reflection(worldNormal, viewDir, albedo, reflection);						// 计算反射
	calculate_refraction(worldNormal, viewDir, albedo, refraction);						// 计算折射

    vec3 ambient 	            = getAmbient(albedo);
	vec3 emission 	            = Material.Emission;
	vec3 diffuse 	            = vec3(0);
	vec3 specular	            = vec3(0);
	phong_illumination(worldNormal, viewDir, input_info.worldPos, diffuse, specular);	// 计算光照

#define SHADING_TYPE 0 // 理想的反射折射方式,但这种方式依赖理想的环境贴图
// #define SHADING_TYPE 1 // 将 emission、specular 分离出来的方式

#if SHADING_TYPE == 0
    vec3 combinedCol            = min(vec3(1.0), ambient + emission + diffuse * albedo + specular);
	combinedCol					= mix(combinedCol, reflection, Material.ReflectionK);	// 应用反射
	combinedCol					= mix(combinedCol, refraction, Material.RefractionK);	// 应用折射
#elif SHADING_TYPE == 1
    vec3 combinedCol            = min(vec3(1.0), ambient + diffuse * albedo);
	combinedCol					= mix(combinedCol, reflection, Material.ReflectionK);	// 应用反射
	combinedCol					= mix(combinedCol, refraction, Material.RefractionK);	// 应用折射

	combinedCol					+= emission + specular;
#endif

	output_col 	            	= vec4(combinedCol, alpha);
}

外部调用

// jave.lin - testing_refraction.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() {
	vec4 out_col;
	ShadingInput_t input_info;
	input_info.tex_col = texture(main_tex, fUV0);
	input_info.normal = fNormal;
	input_info.worldPos = fWorldPos;
	shading(input_info, out_col);
	gl_FragColor = out_col;
}

References

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值