美文网首页
游戏shader(6):利用法线贴图实现动态2D光照效果

游戏shader(6):利用法线贴图实现动态2D光照效果

作者: 小木沐木 | 来源:发表于2021-03-08 17:25 被阅读0次

本文讲解2D游戏中,如何利用法线贴图来实现有材质特性、全角度、且受时间影响的接近真实的光照效果。

本文链接 游戏shader(6):利用法线贴图实现动态2D光照效果

一、制作背景

    2D游戏中,场景中有多个火把、蜡烛、灯光等贴图,让场景非常真实绚丽。但遗憾的是,周围的物体和经过的游戏角色,并不受这些光的影响。

    我们要做的就是让周围的物体和经过的游戏角色,实时地受到这些灯光的影响,实现仿3D效果,增强2D游戏的体验和真实性。

二、效果动态图对比

    以下展示了不受光照影响和受光照影响的效果对比图。很明显,对旁边的火把光作出反馈时,角色更加真实。

图1 角色不受火光影响 图1 角色受火光影响

三、实现原理

    实现基本原理是,我们将环境光源的坐标、颜色、强度实时传入角色的片元shader,据此重新计算角色的颜色。

    那么如何确定角色身上哪些像素接受光照、哪些不接受、又接受多少呢?这个就要用到法线贴图了。在法线贴图上,每个纹理像素的RGB不再代表颜色分量,而是代表角色身上每个像素的法向量信息。关于法线贴图定义请自行查阅相关资料。

    步骤如下:

    1、定义光照影响因子。这里用光源坐标、颜色、强度、时间变量几个uniform变量。

    2、准备法线贴图。一种方法是自定义每个像素的法向量信息,最后导出贴图。一种是利用法线贴图生成工具,这里推荐一个在线法线贴图生成工具  https://cpetry.github.io/NormalMap-Online/  ,上传贴图,调整参数,并生成和下载的法线贴图。这里用的是在线生成。

    3、将法线贴图纹理,作为shader的uniform参数传入供采样。

图3  在线生成法线贴图

四、关键实现和代码

    一些算法做简化处理。思想粗略描述如下:

1、强度距离衰减因子 = 光照强度 × ((感光距离最大值常量 - 角色纹理像素到光源的距离 )/ 感光距离最大值常量);

2、法向量2D平面分量 = vec2(法线贴图颜色.R,法线贴图颜色.B)* 2.0; // 2d游戏中忽略Z分量

3、光照反馈强度因子 = 点积(光源到角色像素的向量, 法向量2D平面分量)

4、时间因子。动态变化以实现强弱闪烁效果。更真实地,应该以光源实际强度的值为准,在CPU去计算。这里简便起见,用时间因子模拟

4、角色像素最终颜色RGB = 角色纹理原始颜色 × (1-光照原始透明度) + 光照原始颜色RGB × 强度距离衰减因子 × 光照反馈强度因子 × 时间影响因子 × (光照原始透明度)

以上混合透明度可以根据自己想要的效果自定义即可。

shader代码如下:

u_timeValue // 传入的时间值,数值自定义,可每帧随机变化,来模拟闪烁效果 。更真实地,应该以光源实际强度的值为准,在CPU去计算。这里简便起见,用时间因子模拟。

vec4 normalColor = texture2D(u_normalTexture, uvn); // 该片元坐标在法线贴图上的采样颜色

vec2 lightPos = u_lightPos; // 光源的位置

vec4 lightColor = u_lightColor; // 光的原始颜色

float lightRadius = u_lightRadius; // 光能照到的最大半径

float px = u_nodePos.x + (uvx -u_nodeAnchor.x) * u_nodeWidth; // 计算角色身上某像素的x位置

float py = u_nodePos.y + (u_nodeAnchor.y - uvy) * u_nodeHeight; // 计算角色身上某像素的y位置

vec2 vecLight = vec2(px - lightPos.x, py - lightPos.y); // 光源到角色身上某像素的向量

vec2 vecLightN = normalize(vecLight); // 光源到角色身上某像素向量的法向量

float dis = length(vecLight); // 光源到角色身上某像素的距离

vec2 normalVec = vec2(normalColor.r - 0.5, normalColor.g - 0.5) * 2.0; // 法线贴图坐标空间转到颜色空间(-1~1转0~1)

if (u_nodeScaleX == -1.0) { // 可能的x翻转

    normalVec.r = -normalVec.r;

}

if (u_texture_flipY > 0.0) { // 可能的y翻转

    normalVec.g = -normalVec.g;

float strength2 = max(dot(vecLightN, normalVec), 0.0);  // 关键点:法向量和vecLightN 向量做点积运算,得到反馈强度值

float strength = smoothstep(0.0, 1.0, 1.0 - dis/lightRadius); // 计算距离衰减因子,这里采用平滑插值函数

float time = u_timeValue / timeRatio; // 时间影响因子

float timeYu = time - float(int(time));// 计算要用到的时间余数

timeYu = timeYu * 0.5 + 0.5 * timeRatio;// 时间转换到0~timeRatio范围

float strength3 = timeYu / timeRatio; // 时间因子转换 0~1

strength3 = strength3 * 0.5 + 0.5; // 时间因子转换 0.5~1 防止闪烁过于强烈很突兀

vec3 mixColor = color.rgb + lightColor.rgb * strength * strength2 * strength3 * lightColor.a; // 最终颜色混合公式.混合方式和参数可以自定义 这里采用颜色直接相加

color = vec4(mixColor, color.a); // 角色像素最终颜色

本文链接 游戏shader(6):利用法线贴图实现动态2D光照效果

相关文章

  • 游戏shader(6):利用法线贴图实现动态2D光照效果

    本文讲解2D游戏中,如何利用法线贴图来实现有材质特性、全角度、且受时间影响的接近真实的光照效果。 本文链接 游戏s...

  • UnityShader Demo01:冰块材质

    简单版本效果如下 原理使用法线贴图扭曲透明颜色贴图的uv值Shader 代码1 surface版本 shader代...

  • Cesium轨迹线

    功能描述 添加自定义material,利用shader和贴图实现轨迹线效果。 示例效果 贴图 自定义材质 使用示例

  • 2022-10-18 实现有倒影的水

    一、原理1.实现环境贴图和漫反射2.利用法线贴图对环境贴图进行干扰,让环境贴图有扭曲的效果3.利用环境贴图代替漫反...

  • Shader编程(三)纹理贴图、法线映射与Alpha透明度

    法线贴图与凹凸映射的强度 编写透明的Shader

  • 二、光照模型:5、法线贴图

    什么是法线贴图技术呢?这是一种用来实现3D效果的一种技术,简单说就是贴图实现凹凸效果。 我们知道,在游戏中经常会有...

  • 【Unity Shader入门精要学习】Surface Shad

    Surface Shader的优缺点 优点: 1、方便,很多工作不用我们自己完成,如法线贴图,正常自己实现可能需要...

  • 凹凸小记

    1.高度贴图转法线贴图2.多张法线贴图效果的叠加3.法线从切线空间转世界空间 1.高度贴图转法线贴图 求出uv,u...

  • Shader笔记-法线贴图

    这个感觉就是游戏开发中优化填坑的一个产物。具体的作用是用来提高低模的精度,体现出更多模型的细节,有种单面模型那种来...

  • Shader学习7——法线贴图

    百度到的法线贴图代码都很乱啊,这里整理一下,实现其实就是读取法线贴图的zw值修改uv顶点的法线方向,从而达到欺骗人...

网友评论

      本文标题:游戏shader(6):利用法线贴图实现动态2D光照效果

      本文链接:https://www.haomeiwen.com/subject/ckiqtltx.html