免費開源!細節拉滿的一個2D實時水面反射效果Demo,基於Cocos Creator 3.5
引言:外掛 Easy NavMesh、BenchMark 效能檢測的作者孫二喵,從開發者王師傅的論壇分享中獲得啟發,實現了 2D 實時水面反射效果,Demo 免費開源。
2D 實時水面反射
Demo 效果
前幾天看到論壇大佬的 2D 水面 Shader 教程(注:後文有詳細內容),效果挺好的,就跟著在 Cocos Creator 3.5.2 中做了一個,實現 2D 實時水面反射,並支援角色移動,開箱即用。
功能特點
整個方案使用了3個攝像機:
RT 攝像機負責所有遊戲物體,它會獲取 Rendertexture 渲染到地面和水面的2個精靈上。
FixCamera 是固定攝像機,只會拍攝地面和水面這2個精靈。
UI 相機負責 UI 層級。
最終的 DrawCall 為8個,包括1個基礎 DC +1個背景+1個角色+2個技能特效+1個 UI+ 1個水面渲染+1個地面渲染。
即使增加1個新的精靈類,我們整個 DrawCall 也只會增加1個。
針對反射方案,第一個 RT 攝像機負責地面部分的擷取,這裡把 rect 設定成了 -0.37 , 只擷取63%的上半部分螢幕。
針對地面部分,我們使用了自定義 Shader,移植了預設的 Sample from RT,使用了和 Canvas 一樣的尺寸,針對螢幕適配,Canvas 上的 rtAdapter 裡有解決方案。
水面部分只使用了一半的螢幕高度,這裡加了 Tiling 的 Macro,設定了 UV。
水浪效果,通過 update wavetexture 的 uv 得到一個偏移量 offset,並於原始圖片的 uv 相加。
波浪效果有了,但是看起來比較單一,這裡希望水面的顏色更有多彩,同時有一點邪惡的小紫色。
加了 casutic 效果後,水面有了焦散的流動。
為了讓水面底部看起來顏色更深,這裡使用了 smoothstep 根據 uv.y 做變化。
為了讓水面上部看起來更通透,彷佛有水波打到岸,對透明度也使用了 smoothstep。
最後給小姐姐加上一個 FSM 狀態機和一個 spriteatlas 的 animator controller,就可以拖到搖桿跑起來啦!
這裡要注意的是,我只移動了 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
最終效果
場景準備
首先弄個乾淨的 Shader,然後布個遊戲場景。
主場景圖
反轉y軸,放在下方做水面用的
擺一下場景
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.);
}
噪聲圖
準備一張噪聲圖:
// 檔案開頭修改加入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 中。看下現在的樣子:
水波效果
接著我們用這個噪聲圖實現水波盪漾的效果。取噪聲圖的 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.);
}
可以看到,畫面明顯扭曲了,但是現在還是靜態的,需要加入時間引數讓這個扭曲動起來。
偏移值的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.);
}
現在,水面波動已經明顯了。
水面明暗
接著實現一下水面的明暗變化,鏡頭越近顏色越暗(其實就是螢幕越往下變顏色越黑)。
color += texture2D(texture,off1 + v_uv0).xyz;
// 引數0-1是正好 從黑到白的,用-.5->1.3這個範圍,色值就是不太黑到不太白,免得顏色太極端
color *= smoothstep(-.5,1.3,v_uv0.y) - .3;
水面浮沫
新增一些水面的浮沫,豐富細節。
這裡又要用噪聲圖了。我們對噪聲圖再來一次取值:
// 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.);
現在看著像海浪,並不是我們想要的浮沫效果。怎麼從這個海浪變換一個浮沫的效果出來呢?
float c1 = texture2D(texture2,baseuv).x;
// 加這一行
c1 = smoothstep(0.23,0.,c1);
color = vec3(c1);
啥原理呢,上面那個大浪啊,值0-1的,用 smoothstep 對大浪做個變換,只留下 0-0.23 的色值,大於 0.23 的都變成了純黑色。
大概畫一個函式曲線:
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);
最終效果
綜合上述幾個實現後,我們得到了最終的呈現效果,完整程式碼:
// 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
相關文章
- 再加億點點細節!Cocos 基於平面著色的 3D 水面渲染方案3D
- 推薦一個基於laravel免費開源CMSLaravel
- Cocos Creator實戰-使用粒子資源實現螢幕點選效果
- 進階!Cocos Creator 中使用模板測試實現遮罩效果遮罩
- [開源免費]一個 AI 聯網搜尋,使用全免費資源實現AI
- 推薦一個開源免費的 Spring Boot 實戰專案Spring Boot
- Cocos Creator 新資源管理系統剖析
- Cocos Creator 3D 材質系統:曲面效果如何實現?3D
- Cocos Creator踩坑日記(一)
- 一個基於Android的MVP框架DemoAndroidMVP框架
- 1024|推薦一個開源免費的Spring Boot教程Spring Boot
- Cocos Creator 資源載入流程剖析【三】——Load部分
- 開源=免費?
- 免費開源TuziCMS基於ThinkPHP的企業網站管理系統PHP網站
- 工序升級!在 Cocos Creator 裡畫一個完美的矩形
- C# 開源一個基於 yarp 的 API 閘道器 Demo,支援繫結 Kubernetes ServiceC#API
- Cocos Creator 的工作流程 2.4
- Mind elixir 一個免費開源的思維導圖核心
- Laravel+vue免費開源的基於RABC控制的部落格系統LaravelVue
- 基於 go 語言開發部署的部落格 免費開源供參考Go
- 【永久開源】layuimini,一款基於 layui 華麗免費的 admin 管理後臺模板。UI
- Cocos Creator 資源載入流程剖析【五】——從編輯器到執行時
- 超越輔助:分享一個基於GPT引擎的免費AI工具GPTAI
- Cocos Creator - 微信小遊戲 實戰分享遊戲
- 原始碼推薦:基於uni-app前端框架,開源版本還開源免費商用原始碼APP前端框架
- Cocos Creator 3.x缺陷
- 小遊戲開發上手體驗 – Cocos Creator遊戲開發
- Unity Shader 實現雨天的水面漣漪效果Unity
- 如何開發一個免費的app?APP
- Cocos Creator 入門篇-拖拽小遊戲(一)遊戲
- 一個基於 Vue3 的開源專案,3個月時間 star 終於破千!Vue
- 基於ThinkPHP5.0+GatewayWorker寫的一個聊天DEMOPHPGateway
- 開源等於免費嗎?真相在這裡
- 細節拉滿,80 張圖帶你一步一步推演 slab 記憶體池的設計與實現記憶體
- 上雲節省 35%計算資源,420 個運維人天:運滿滿實時計算實踐和思考運維
- 使用Spring Boot開發的10個免費開源專案Spring Boot
- cocos creator中適配iPhone XiPhone
- 開源免費的建站系統