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

本文讲解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光照效果

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,042评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,996评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,674评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,340评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,404评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,749评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,902评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,662评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,110评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,577评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,258评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,848评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,726评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,952评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,271评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,452评论 2 348

推荐阅读更多精彩内容