免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

孫二喵、王師傅發表於2022-07-15
引言:外掛 Easy NavMesh、BenchMark 效能檢測的作者孫二喵,從開發者王師傅的論壇分享中獲得啟發,實現了 2D 實時水面反射效果,Demo 免費開源。

2D 實時水面反射

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5
Demo 效果

前幾天看到論壇大佬的 2D 水面 Shader 教程(注:後文有詳細內容),效果挺好的,就跟著在 Cocos Creator 3.5.2 中做了一個,實現 2D 實時水面反射,並支援角色移動,開箱即用。

功能特點

整個方案使用了3個攝像機:

RT 攝像機負責所有遊戲物體,它會獲取 Rendertexture 渲染到地面和水面的2個精靈上。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

FixCamera 是固定攝像機,只會拍攝地面和水面這2個精靈。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

UI 相機負責 UI 層級。

最終的 DrawCall 為8個,包括1個基礎 DC +1個背景+1個角色+2個技能特效+1個 UI+ 1個水面渲染+1個地面渲染。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

即使增加1個新的精靈類,我們整個 DrawCall 也只會增加1個。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

針對反射方案,第一個 RT 攝像機負責地面部分的擷取,這裡把 rect 設定成了 -0.37 , 只擷取63%的上半部分螢幕。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

針對地面部分,我們使用了自定義 Shader,移植了預設的 Sample from RT,使用了和 Canvas 一樣的尺寸,針對螢幕適配,Canvas 上的 rtAdapter 裡有解決方案。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

水面部分只使用了一半的螢幕高度,這裡加了 Tiling 的 Macro,設定了 UV。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

水浪效果,通過 update wavetexture 的 uv 得到一個偏移量 offset,並於原始圖片的 uv 相加。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

波浪效果有了,但是看起來比較單一,這裡希望水面的顏色更有多彩,同時有一點邪惡的小紫色。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

加了 casutic 效果後,水面有了焦散的流動。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

為了讓水面底部看起來顏色更深,這裡使用了 smoothstep 根據 uv.y 做變化。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

為了讓水面上部看起來更通透,彷佛有水波打到岸,對透明度也使用了 smoothstep。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

最後給小姐姐加上一個 FSM 狀態機和一個 spriteatlas 的 animator controller,就可以拖到搖桿跑起來啦!

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

這裡要注意的是,我只移動了 RT 相機,沒有移動水面的 renderer,所以水面會看起來比較奇怪,可以在人物移動的時候,給水面的水波和焦散一個反方向(下次一定,下次一定)。

資源連結

點選文末【閱讀原文】下載 Demo:
https://store.cocos.com/app/detail/3900

論壇專貼:
https://forum.cocos.org/t/topic/137681

實現過程

以下為社群開發者「王師傅」釋出在論壇的技術分享。作者花式使用噪聲圖實現了 2D 水面波浪效果,這也是上文孫二喵 Demo 的方案參考,感興趣的小夥伴可以閱讀一下。帖子地址:

https://forum.cocos.org/t/topic/121407

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5
最終效果

場景準備

首先弄個乾淨的 Shader,然後布個遊戲場景。

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5
主場景圖

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5
反轉y軸,放在下方做水面用的

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5
擺一下場景

NewSprite 縮放是 -0.55 ,負數是為了反轉 y 軸做映象效果。

NewSprite 用上我們新建的乾淨的 Shader 和材質,Shader 刪掉片元著色器我們不準備用的程式碼,老規矩,從一張黑圖開始。

precision highp float;

#include <alpha-test>

#include <texture>

#include <cc-global>

in vec4 v_color;

#if USE_TEXTURE

in vec2 v_uv0;

uniform sampler2D texture;

#endif

void main () {

vec3 color = vec3(0.);

// 弄個t接收cc_time.x, *= 0.6是因為正常速太快,變慢點 備用

float t = cc_time.x * 0.6;

gl_FragColor = vec4(color,1.);

}

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

噪聲圖

準備一張噪聲圖:

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

// 檔案開頭修改加入texture2的宣告

properties:

texture: { value: white }

// 新增下面的行 本行註釋刪掉,寫在這裡只是為了標識這是新增的

texture2: {  value: white }

....

....

// 片元著色器修改成下面這樣:

.....

uniform sampler2D texture;

// 新增下面的行

uniform sampler2D texture2;

void main () {

vec3 color = vec3(0.);

// 弄個t接收cc_time.x, *= 0.6是因為正常速太快,變慢點

float t = cc_time.x * 0.6;

// 對噪聲圖取樣x通道,顯示在螢幕上

color += texture2D(texture2,v_uv0).x;

gl_FragColor = vec4(color,1.);

}

然後開啟點選材質,把噪聲圖拖拽到材質皮膚中的 texture2 中。看下現在的樣子:

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

水波效果

接著我們用這個噪聲圖實現水波盪漾的效果。取噪聲圖的 xy 兩個通道,作為對場景圖取樣所用 uv 的偏移值,會出來什麼效果呢?

片元著色器 main 函式改成下面這樣:

void main () {

vec3 color = vec3(0.);

// 弄個t接收cc_time.x, *= 0.6是因為正常速太快,變慢點

float t = cc_time.x * 0.6;

// + t * vec2(.5,.1)

vec2 off1 = texture2D(texture2,v_uv0).xy;

// 偏移值縮放0.1倍,不然波紋太過分了

off1 *= .1;

color += texture2D(texture,off1 + v_uv0).xyz;

gl_FragColor = vec4(color,1.);

}

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

可以看到,畫面明顯扭曲了,但是現在還是靜態的,需要加入時間引數讓這個扭曲動起來。

偏移值的0.1的縮放值還是太大,下面用0.01的試試:

void main () {

vec3 color = vec3(0.);

// 弄個t接收cc_time.x, *= 0.6是因為正常速太快,變慢點

float t = cc_time.x * 0.6;

// v_uv0 + t * vec2(.5,.1) 這裡的t是上面的時間,會持續增大的一個數值

// vec2(.5,.1) 是我隨便寫的一個方向向量,方向*時間 作為uv的偏移

// 噪聲圖設定 WrapMode = Repeat 否則偏移值超過vec2(1.,1.)之後就取不到值了,要改成repeat才可以取值

vec2 off1 = texture2D(texture2,v_uv0 + t * vec2(.5,.1)).xy;

// 偏移值縮放0.1倍,不然波紋太過分了

off1 *= .01;

color += texture2D(texture,off1 + v_uv0).xyz;

gl_FragColor = vec4(color,1.);

}

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

現在,水面波動已經明顯了。

水面明暗

接著實現一下水面的明暗變化,鏡頭越近顏色越暗(其實就是螢幕越往下變顏色越黑)。

color += texture2D(texture,off1 + v_uv0).xyz;

// 引數0-1是正好 從黑到白的,用-.5->1.3這個範圍,色值就是不太黑到不太白,免得顏色太極端

color *= smoothstep(-.5,1.3,v_uv0.y) - .3;

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

水面浮沫

新增一些水面的浮沫,豐富細節。

這裡又要用噪聲圖了。我們對噪聲圖再來一次取值:

// baseuv做變換對噪聲圖取值

vec2 baseuv = v_uv0;

// 讓噪聲圖x軸重複四次,y軸重複三次

vec2 scale = vec2(4.,3.);

baseuv = baseuv * scale;

float c1 = texture2D(texture2,baseuv).x;

color = vec3(c1);

gl_FragColor = vec4(color,1.);

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

現在看著像海浪,並不是我們想要的浮沫效果。怎麼從這個海浪變換一個浮沫的效果出來呢?

float c1 = texture2D(texture2,baseuv).x;

// 加這一行

c1 = smoothstep(0.23,0.,c1);

color = vec3(c1);

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

啥原理呢,上面那個大浪啊,值0-1的,用 smoothstep 對大浪做個變換,只留下 0-0.23 的色值,大於 0.23 的都變成了純黑色。

大概畫一個函式曲線:

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

c1 值輸入函式曲線後,0-0.23範圍輸出了 0->1 的值,越黑的地方越亮,亮的就變黑色了,也就得到了上面圖的效果。

接著讓浮沫漂流起來。思考一下,如果讓浮沫整體往一個方向漂是不是有點太單調太假了,不行,要做分行速度差。分20行吧,每行做移動速度不同的漂流:

float random (vec2 st) {

return fract(sin(dot(st.xy,

vec2(12.9898,78.233)))*

43758.5453123);

}

......

上面的函式加在main函式外面

下面的東西是main裡面的

......

// 白沫的uv 所以取名mouv!

vec2 mouv = v_uv0;

// 做y軸分行,原理見彩虹那篇帖子

mouv.y *= 20.;

// n3就是行id 取名無力

float n3 = floor(mouv.y);

// n4就是用行id隨機一個對應的隨機值出來,每行一個隨機值作為行的運動速度

// 所以對於y軸在同一行(注意上面*=20,所以共20行),我們這裡計算出一個改行的運動速度,放在這備用

float n4 = random(vec2(n3,n3)) + .3;

// baseuv做變換對噪聲圖取值

vec2 baseuv = v_uv0;

// 讓噪聲圖x軸重複四次,y軸重複三次

vec2 scale = vec2(4.,3.);

baseuv = baseuv * scale;

// 取值用的uv加上向左的移動 t是上面用過的那個cc_time.x哈,*0.1不然太快了

// n4是上面計算好的速度哈

baseuv.x += t * .1 * n4;

float c1 = texture2D(texture2,baseuv).x;

c1 = smoothstep(0.23,0.,c1);

color = vec3(c1);

免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5

最終效果

綜合上述幾個實現後,我們得到了最終的呈現效果,完整程式碼:

// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.

CCEffect %{

techniques:

- passes:

- vert: vs

frag: fs

blendState:

targets:

- blend: true

rasterizerState:

cullMode: none

properties:

texture: { value: white }

texture2: {  value: white }

alphaThreshold: { value: 0.5 }

}%

CCProgram vs %{

precision highp float;

#include <cc-global>

#include <cc-local>

in vec3 a_position;

in vec4 a_color;

out vec4 v_color;

#if USE_TEXTURE

in vec2 a_uv0;

out vec2 v_uv0;

#endif

void main () {

vec4 pos = vec4(a_position, 1);

#if CC_USE_MODEL

pos = cc_matViewProj * cc_matWorld * pos;

#else

pos = cc_matViewProj * pos;

#endif

#if USE_TEXTURE

v_uv0 = a_uv0;

#endif

v_color = a_color;

gl_Position = pos;

}

}%

CCProgram fs %{

precision highp float;

#include <alpha-test>

#include <texture>

#include <cc-global>

in vec4 v_color;

in vec2 v_uv0;

uniform sampler2D texture;

uniform sampler2D texture2;

float random (vec2 st) {

return fract(sin(dot(st.xy,

vec2(12.9898,78.233)))*

43758.5453123);

}

void main () {

vec3 color = vec3(0.);

// 弄個t接收cc_time.x, *= 0.6是因為正常速太快,變慢點

float t = cc_time.x * 0.6;

// v_uv0 + t * vec2(.5,.1) 這裡的t是上面的時間,會持續增大的一個數值

// vec2(.5,.1) 是我隨便寫的一個方向向量,方向*時間 作為uv的偏移

// 噪聲圖設定 WrapMode = Repeat 否則偏移值超過vec2(1.,1.)之後就取不到值了,要改成repeat才可以取值

vec2 off1 = texture2D(texture2,v_uv0 + t * vec2(.5,.1)).xy;

// 偏移值縮放0.1倍,不然波紋太過分了

off1 *= .01;

color += texture2D(texture,off1 + v_uv0).xyz;

// 引數0-1是正好 從黑到白的,用-.5->1.3這個範圍,色值就是不太黑到不太白,免得顏色太極端

color *= smoothstep(-.5,1.3,v_uv0.y) - .3;

// 白沫的uv 所以取名mouv!

vec2 mouv = v_uv0;

// 做y軸分行,原理見彩虹那篇帖子

mouv.y *= 20.;

// n3就是行id 取名無力

float n3 = floor(mouv.y);

// n4就是用行id隨機一個對應的隨機值出來,每行一個隨機值作為行的運動速度

// 所以對於y軸在同一行(注意上面*=20,所以共20行),我們這裡計算出一個改行的運動速度,放在這備用

float n4 = random(vec2(n3,n3)) + .3;

// baseuv做變換對噪聲圖取值

vec2 baseuv = v_uv0;

// 讓噪聲圖x軸重複四次,y軸重複三次

vec2 scale = vec2(4.,3.);

baseuv = baseuv * scale;

// 取值用的uv加上向左的移動 t是上面用過的那個cc_time.x哈,*0.1不然太快了

// n4是上面計算好的速度哈

baseuv.x += t * .1 * n4;

float c1 = texture2D(texture2,baseuv).x;

c1 = smoothstep(0.23,0.,c1);

color += vec3(c1);

gl_FragColor = vec4(color,1.);

}

}%


來源:COCOS
原文:https://mp.weixin.qq.com/s/o319kxdbEunkQsloDu0rEA


相關文章