diff --git a/README.md b/README.md index 0b56d6c..5265853 100755 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ cocos是一款挺棒的游戏引擎,我在这个仓库实现一些游戏当中 # 目前已实现效果 +[水波扩散(shader)](./doc/Water_spread/水波扩散效果.md) [镜面光泽(shader)](./doc/Specular_gloss/镜面光泽.md) [圆形头像(shader)](./doc/Circle_avatar/shader圆形头像.md) [溶解效果(shader)](./doc/Dissolve_color/溶解效果.md) @@ -24,6 +25,11 @@ cocos是一款挺棒的游戏引擎,我在这个仓库实现一些游戏当中 2D换装 震屏效果+动画恢复第一帧 +## 水波扩散(shader) +水波扩散是一个比较好看的交互效果,特别是在某些以水为故事发生场景的游戏中,扩散的水波会让场景更加栩栩如生。 + +![demo](.doc/Water_spread/resource/demo.gif) + ## 镜面光泽(shader) 镜面上扫过一道光泽是UI里面很常用的一种特效,通常用来强调某个物体或者凸显物体的“稀有”价值,比如卡片中扫过一道光芒等 diff --git a/assets/Scene/Home/Home.ts b/assets/Scene/Home/Home.ts index 39385df..4925dc5 100644 --- a/assets/Scene/Home/Home.ts +++ b/assets/Scene/Home/Home.ts @@ -2,16 +2,17 @@ import BackHomeBtn from './BackHomeBtn'; const { ccclass, property } = cc._decorator; const LOAD_SCENE_MIN_SEC: number = 1.2; enum sceneList { + 'Water_spread' = '水波扩散效果(shader)', 'Specular_gloss' = '镜面光泽效果(shader)', 'Dissolve_color' = '溶解效果(shader)', + 'Follow_spot' = '追光效果(shader)', + 'Circle_avatar' = '圆形头像(shader)', 'Scratch_ticket' = '刮刮卡实现', 'Coin_fly_to_wallet' = '金币落袋效果', + 'Moving_ghost' = '移动残影效果', 'Magnifying_mirror' = '放大镜效果', - 'Follow_spot' = '追光效果(shader)', 'Typer' = '打字机效果', 'Bullet_Tracking' = '子弹跟踪效果', - 'Moving_ghost' = '移动残影效果', - 'Circle_avatar' = '圆形头像(shader)', 'Infinite_bg_scroll' = '背景无限滚动', 'Change_clothes' = '换装', 'Screen_vibrating' = '震屏效果+动画恢复第一帧', diff --git a/assets/Scene/Water_spread.meta b/assets/Scene/Water_spread.meta new file mode 100644 index 0000000..37bca04 --- /dev/null +++ b/assets/Scene/Water_spread.meta @@ -0,0 +1,7 @@ +{ + "ver": "1.0.1", + "uuid": "301c3c85-5e2b-4f37-b5ee-96a6c5ca69d2", + "isSubpackage": false, + "subpackageName": "", + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Scene/Water_spread/Water_spread.effect b/assets/Scene/Water_spread/Water_spread.effect new file mode 100644 index 0000000..ea0a307 --- /dev/null +++ b/assets/Scene/Water_spread/Water_spread.effect @@ -0,0 +1,93 @@ +// 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 } + canvas_size: { value: [ 667.0, 375.0 ] } + center: { value: [ 0.5, 0.5 ] } + wave_radius: { value: 0.18 } + wave_offset: { value: 2.0 } +}% + + +CCProgram vs %{ + precision highp float; + + #include + #include + + 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 + #include + + in vec4 v_color; + + #if USE_TEXTURE + in vec2 v_uv0; + uniform sampler2D texture; + #endif + + uniform ARGS{ + vec2 center; + vec2 canvas_size; + float wave_radius; + float wave_offset; + }; + + void main() { + vec2 distance_vec = center - v_uv0; + distance_vec = distance_vec * vec2(canvas_size.x / canvas_size.y, 1.0); + float distance = sqrt(distance_vec.x * distance_vec.x + distance_vec.y * distance_vec.y); + + // distance小于1,但是我们希望能有多个波峰波谷,所以在sin的内部乘上一个比较大的倍数 + // sin函数的值在-1到1之间,我们希望偏移值很小,所以输出的时候需要缩小一定的倍数倍 + float sin_factor = sin(distance * 100.0 + cc_time.x) * 0.05; + float discard_factor = clamp(wave_radius - abs(wave_offset - distance), 0.0, 1.0); + + // 计算总的uv的偏移值 + vec2 offset = normalize(distance_vec) * sin_factor * discard_factor; + vec2 uv = offset + v_uv0; + + gl_FragColor = texture(texture, uv); + } +}% diff --git a/assets/Scene/Water_spread/Water_spread.effect.meta b/assets/Scene/Water_spread/Water_spread.effect.meta new file mode 100644 index 0000000..127882c --- /dev/null +++ b/assets/Scene/Water_spread/Water_spread.effect.meta @@ -0,0 +1,17 @@ +{ + "ver": "1.0.25", + "uuid": "7b534a3f-8819-43a5-87dc-5d55a6869117", + "compiledShaders": [ + { + "glsl1": { + "vert": "\nprecision highp float;\nuniform mediump mat4 cc_matViewProj;\nuniform mat4 cc_matWorld;\nattribute vec3 a_position;\nattribute vec4 a_color;\nvarying vec4 v_color;\n#if USE_TEXTURE\nattribute vec2 a_uv0;\nvarying vec2 v_uv0;\n#endif\nvoid main () {\n vec4 pos = vec4(a_position, 1);\n #if CC_USE_MODEL\n pos = cc_matViewProj * cc_matWorld * pos;\n #else\n pos = cc_matViewProj * pos;\n #endif\n #if USE_TEXTURE\n v_uv0 = a_uv0;\n #endif\n v_color = a_color;\n gl_Position = pos;\n}", + "frag": "\nprecision highp float;\n#if USE_ALPHA_TEST\n#endif\nuniform highp vec4 cc_time;\nvarying vec4 v_color;\n#if USE_TEXTURE\nvarying vec2 v_uv0;\nuniform sampler2D texture;\n#endif\nuniform vec2 center;\nuniform vec2 canvas_size;\nuniform float wave_radius;\nuniform float wave_offset;\nvoid main() {\n vec2 distance_vec = center - v_uv0;\n distance_vec = distance_vec * vec2(canvas_size.x / canvas_size.y, 1.0);\n float distance = sqrt(distance_vec.x * distance_vec.x + distance_vec.y * distance_vec.y);\n float sin_factor = sin(distance * 100.0 + cc_time.x) * 0.05;\n float discard_factor = clamp(wave_radius - abs(wave_offset - distance), 0.0, 1.0);\n vec2 offset = normalize(distance_vec) * sin_factor * discard_factor;\n vec2 uv = offset + v_uv0;\n gl_FragColor = texture2D(texture, uv);\n}" + }, + "glsl3": { + "vert": "\nprecision highp float;\nuniform CCGlobal {\n highp vec4 cc_time;\n mediump vec4 cc_screenSize;\n mediump vec4 cc_screenScale;\n mediump vec4 cc_nativeSize;\n highp mat4 cc_matView;\n mediump mat4 cc_matViewInv;\n mediump mat4 cc_matProj;\n mediump mat4 cc_matProjInv;\n mediump mat4 cc_matViewProj;\n mediump mat4 cc_matViewProjInv;\n mediump vec4 cc_cameraPos;\n};\nuniform CCLocal {\n mat4 cc_matWorld;\n mat4 cc_matWorldIT;\n};\nin vec3 a_position;\nin vec4 a_color;\nout vec4 v_color;\n#if USE_TEXTURE\nin vec2 a_uv0;\nout vec2 v_uv0;\n#endif\nvoid main () {\n vec4 pos = vec4(a_position, 1);\n #if CC_USE_MODEL\n pos = cc_matViewProj * cc_matWorld * pos;\n #else\n pos = cc_matViewProj * pos;\n #endif\n #if USE_TEXTURE\n v_uv0 = a_uv0;\n #endif\n v_color = a_color;\n gl_Position = pos;\n}", + "frag": "\nprecision highp float;\n#if USE_ALPHA_TEST\n uniform ALPHA_TEST {\n float alphaThreshold;\n };\n#endif\nuniform CCGlobal {\n highp vec4 cc_time;\n mediump vec4 cc_screenSize;\n mediump vec4 cc_screenScale;\n mediump vec4 cc_nativeSize;\n highp mat4 cc_matView;\n mediump mat4 cc_matViewInv;\n mediump mat4 cc_matProj;\n mediump mat4 cc_matProjInv;\n mediump mat4 cc_matViewProj;\n mediump mat4 cc_matViewProjInv;\n mediump vec4 cc_cameraPos;\n};\nin vec4 v_color;\n#if USE_TEXTURE\nin vec2 v_uv0;\nuniform sampler2D texture;\n#endif\nuniform ARGS{\n vec2 center;\n vec2 canvas_size;\n float wave_radius;\n float wave_offset;\n};\nvoid main() {\n vec2 distance_vec = center - v_uv0;\n distance_vec = distance_vec * vec2(canvas_size.x / canvas_size.y, 1.0);\n float distance = sqrt(distance_vec.x * distance_vec.x + distance_vec.y * distance_vec.y);\n float sin_factor = sin(distance * 100.0 + cc_time.x) * 0.05;\n float discard_factor = clamp(wave_radius - abs(wave_offset - distance), 0.0, 1.0);\n vec2 offset = normalize(distance_vec) * sin_factor * discard_factor;\n vec2 uv = offset + v_uv0;\n gl_FragColor = texture(texture, uv);\n}" + } + } + ], + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Scene/Water_spread/Water_spread.fire b/assets/Scene/Water_spread/Water_spread.fire new file mode 100644 index 0000000..38fab07 --- /dev/null +++ b/assets/Scene/Water_spread/Water_spread.fire @@ -0,0 +1,417 @@ +[ + { + "__type__": "cc.SceneAsset", + "_name": "", + "_objFlags": 0, + "_native": "", + "scene": { + "__id__": 1 + } + }, + { + "__type__": "cc.Scene", + "_objFlags": 0, + "_parent": null, + "_children": [ + { + "__id__": 2 + } + ], + "_active": true, + "_components": [], + "_prefab": null, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 0, + "height": 0 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1 + ] + }, + "_is3DNode": true, + "_groupIndex": 0, + "groupIndex": 0, + "autoReleaseAssets": false, + "_id": "b3a76f79-b230-4227-a380-f595499d9f43" + }, + { + "__type__": "cc.Node", + "_name": "Canvas", + "_objFlags": 0, + "_parent": { + "__id__": 1 + }, + "_children": [ + { + "__id__": 3 + }, + { + "__id__": 5 + } + ], + "_active": true, + "_components": [ + { + "__id__": 8 + }, + { + "__id__": 9 + }, + { + "__id__": 10 + } + ], + "_prefab": null, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 1334, + "height": 750 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 667, + 375, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1 + ] + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "a5esZu+45LA5mBpvttspPD" + }, + { + "__type__": "cc.Node", + "_name": "Main Camera", + "_objFlags": 0, + "_parent": { + "__id__": 2 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 4 + } + ], + "_prefab": null, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 1334, + "height": 750 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 0, + 0, + 452.93128617926146, + 0, + 0, + 0, + 1, + 1, + 1, + 1 + ] + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "e1WoFrQ79G7r4ZuQE3HlNb" + }, + { + "__type__": "cc.Camera", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 3 + }, + "_enabled": true, + "_cullingMask": 4294967295, + "_clearFlags": 7, + "_backgroundColor": { + "__type__": "cc.Color", + "r": 0, + "g": 0, + "b": 0, + "a": 255 + }, + "_depth": -1, + "_zoomRatio": 1, + "_targetTexture": null, + "_fov": 60, + "_orthoSize": 10, + "_nearClip": 1, + "_farClip": 4096, + "_ortho": true, + "_rect": { + "__type__": "cc.Rect", + "x": 0, + "y": 0, + "width": 1, + "height": 1 + }, + "_renderStages": 1, + "_alignWithScreen": true, + "_id": "81GN3uXINKVLeW4+iKSlim" + }, + { + "__type__": "cc.Node", + "_name": "bg", + "_objFlags": 0, + "_parent": { + "__id__": 2 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 6 + }, + { + "__id__": 7 + } + ], + "_prefab": null, + "_opacity": 255, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 1334, + "height": 750 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_trs": { + "__type__": "TypedArray", + "ctor": "Float64Array", + "array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1 + ] + }, + "_eulerAngles": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_skewX": 0, + "_skewY": 0, + "_is3DNode": false, + "_groupIndex": 0, + "groupIndex": 0, + "_id": "1dJPAcHYlFQ4aCCFZiEdFj" + }, + { + "__type__": "cc.Sprite", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 5 + }, + "_enabled": true, + "_materials": [ + { + "__uuid__": "56406760-685c-4c29-8e07-797a69892d89" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, + "_spriteFrame": { + "__uuid__": "861239dd-0928-4f09-a3e6-80b190241a5a" + }, + "_type": 0, + "_sizeMode": 0, + "_fillType": 0, + "_fillCenter": { + "__type__": "cc.Vec2", + "x": 0, + "y": 0 + }, + "_fillStart": 0, + "_fillRange": 0, + "_isTrimmedMode": true, + "_atlas": null, + "_id": "e6sVabYOVDI4mMH27Jv3Rw" + }, + { + "__type__": "cc.Widget", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 5 + }, + "_enabled": true, + "alignMode": 1, + "_target": null, + "_alignFlags": 45, + "_left": 0, + "_right": 0, + "_top": 0, + "_bottom": 0, + "_verticalCenter": 0, + "_horizontalCenter": 0, + "_isAbsLeft": true, + "_isAbsRight": true, + "_isAbsTop": true, + "_isAbsBottom": true, + "_isAbsHorizontalCenter": true, + "_isAbsVerticalCenter": true, + "_originalWidth": 1136, + "_originalHeight": 640, + "_id": "ad0dfC/W5I4bszwgOJLukH" + }, + { + "__type__": "cc.Canvas", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 2 + }, + "_enabled": true, + "_designResolution": { + "__type__": "cc.Size", + "width": 1334, + "height": 750 + }, + "_fitWidth": false, + "_fitHeight": true, + "_id": "59Cd0ovbdF4byw5sbjJDx7" + }, + { + "__type__": "cc.Widget", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 2 + }, + "_enabled": true, + "alignMode": 1, + "_target": null, + "_alignFlags": 45, + "_left": 0, + "_right": 0, + "_top": 0, + "_bottom": 0, + "_verticalCenter": 0, + "_horizontalCenter": 0, + "_isAbsLeft": true, + "_isAbsRight": true, + "_isAbsTop": true, + "_isAbsBottom": true, + "_isAbsHorizontalCenter": true, + "_isAbsVerticalCenter": true, + "_originalWidth": 0, + "_originalHeight": 0, + "_id": "29zXboiXFBKoIV4PQ2liTe" + }, + { + "__type__": "d485aKJH4VK9ZttqIWBQAMc", + "_name": "", + "_objFlags": 0, + "node": { + "__id__": 2 + }, + "_enabled": true, + "bg": { + "__id__": 5 + }, + "_id": "94cGIk+DBBuL8SyDgnW0Bi" + } +] \ No newline at end of file diff --git a/assets/Scene/Water_spread/Water_spread.fire.meta b/assets/Scene/Water_spread/Water_spread.fire.meta new file mode 100644 index 0000000..e3afea0 --- /dev/null +++ b/assets/Scene/Water_spread/Water_spread.fire.meta @@ -0,0 +1,7 @@ +{ + "ver": "1.2.6", + "uuid": "b3a76f79-b230-4227-a380-f595499d9f43", + "asyncLoadAssets": false, + "autoReleaseAssets": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Scene/Water_spread/Water_spread.mtl b/assets/Scene/Water_spread/Water_spread.mtl new file mode 100644 index 0000000..dfc3c9f --- /dev/null +++ b/assets/Scene/Water_spread/Water_spread.mtl @@ -0,0 +1,21 @@ +{ + "__type__": "cc.Material", + "_name": "Water_spread", + "_objFlags": 0, + "_native": "", + "_effectAsset": { + "__uuid__": "7b534a3f-8819-43a5-87dc-5d55a6869117" + }, + "_techniqueIndex": 0, + "_techniqueData": { + "0": { + "props": { + "wave_radius": 0.2, + "wave_offset": 2 + }, + "defines": { + "USE_TEXTURE": true + } + } + } +} \ No newline at end of file diff --git a/assets/Scene/Water_spread/Water_spread.mtl.meta b/assets/Scene/Water_spread/Water_spread.mtl.meta new file mode 100644 index 0000000..bfbd32d --- /dev/null +++ b/assets/Scene/Water_spread/Water_spread.mtl.meta @@ -0,0 +1,6 @@ +{ + "ver": "1.0.3", + "uuid": "56406760-685c-4c29-8e07-797a69892d89", + "dataAsSubAsset": null, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Scene/Water_spread/Water_spread.ts b/assets/Scene/Water_spread/Water_spread.ts new file mode 100644 index 0000000..a58e854 --- /dev/null +++ b/assets/Scene/Water_spread/Water_spread.ts @@ -0,0 +1,27 @@ +const { ccclass, property } = cc._decorator; + +@ccclass +export default class Water_spread extends cc.Component { + @property(cc.Node) + bg: cc.Node = null; + material: cc.Material = null; + + onLoad() { + this.material = this.bg.getComponent(cc.Sprite).getMaterial(0); + this.bg.on(cc.Node.EventType.TOUCH_END, this.touchStartEvent, this); + } + + waveOffset: number = 0.0; + touchStartEvent(evt: cc.Event.EventTouch) { + let pos = evt.getLocation(); + this.material.setProperty('center', [ pos.x / this.bg.width, (this.bg.height - pos.y) / this.bg.height]); + this.waveOffset = 0.0; + } + + update(dt) { + if (this.waveOffset > 2.0) return; + + this.waveOffset += dt; + this.material.setProperty('wave_offset', this.waveOffset); + } +} diff --git a/assets/Scene/Water_spread/Water_spread.ts.meta b/assets/Scene/Water_spread/Water_spread.ts.meta new file mode 100644 index 0000000..1500194 --- /dev/null +++ b/assets/Scene/Water_spread/Water_spread.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.5", + "uuid": "d485a289-1f85-4af5-9b6d-a8858140031c", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/assets/Scene/Water_spread/bg.jpg b/assets/Scene/Water_spread/bg.jpg new file mode 100644 index 0000000..173abaa Binary files /dev/null and b/assets/Scene/Water_spread/bg.jpg differ diff --git a/assets/Scene/Water_spread/bg.jpg.meta b/assets/Scene/Water_spread/bg.jpg.meta new file mode 100644 index 0000000..1834d92 --- /dev/null +++ b/assets/Scene/Water_spread/bg.jpg.meta @@ -0,0 +1,36 @@ +{ + "ver": "2.3.4", + "uuid": "ab5f2507-79c8-4a89-aa11-45a5b3c8a8a7", + "type": "sprite", + "wrapMode": "clamp", + "filterMode": "bilinear", + "premultiplyAlpha": false, + "genMipmaps": false, + "packable": false, + "width": 1024, + "height": 768, + "platformSettings": {}, + "subMetas": { + "bg": { + "ver": "1.0.4", + "uuid": "861239dd-0928-4f09-a3e6-80b190241a5a", + "rawTextureUuid": "ab5f2507-79c8-4a89-aa11-45a5b3c8a8a7", + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 1024, + "height": 768, + "rawWidth": 1024, + "rawHeight": 768, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "subMetas": {} + } + } +} \ No newline at end of file diff --git a/doc/Water_spread/resource/demo.gif b/doc/Water_spread/resource/demo.gif new file mode 100644 index 0000000..4b44db3 Binary files /dev/null and b/doc/Water_spread/resource/demo.gif differ diff --git a/doc/Water_spread/resource/demo2.gif b/doc/Water_spread/resource/demo2.gif new file mode 100644 index 0000000..193a3ab Binary files /dev/null and b/doc/Water_spread/resource/demo2.gif differ diff --git a/doc/Water_spread/resource/demo3.gif b/doc/Water_spread/resource/demo3.gif new file mode 100644 index 0000000..fe2f5f3 Binary files /dev/null and b/doc/Water_spread/resource/demo3.gif differ diff --git a/doc/Water_spread/resource/demo4.gif b/doc/Water_spread/resource/demo4.gif new file mode 100644 index 0000000..501fd6e Binary files /dev/null and b/doc/Water_spread/resource/demo4.gif differ diff --git a/doc/Water_spread/resource/ewm.png b/doc/Water_spread/resource/ewm.png new file mode 100644 index 0000000..e666769 Binary files /dev/null and b/doc/Water_spread/resource/ewm.png differ diff --git a/doc/Water_spread/resource/stretch.png b/doc/Water_spread/resource/stretch.png new file mode 100644 index 0000000..2bd8cda Binary files /dev/null and b/doc/Water_spread/resource/stretch.png differ diff --git a/doc/Water_spread/水波扩散效果.md b/doc/Water_spread/水波扩散效果.md new file mode 100644 index 0000000..3204347 --- /dev/null +++ b/doc/Water_spread/水波扩散效果.md @@ -0,0 +1,91 @@ +# 效果演示 + +水波扩散是一个比较好看的交互效果,特别是在某些以水为故事发生场景的游戏中,扩散的水波会让场景更加栩栩如生。 + +![demo](./resource/demo.gif) + +# 实现思路 + +如果水波静止,我们看到的其实是像素点围绕着某个中心点的拉伸效果,我们只需让每个像素点叠加上它和中心点的向量差,就能够呈现出画面上的所有像素围绕中心点的拉伸感。 +```c++ +void main() { + vec2 uv = normalize(vec2(0.5, 0.5) - v_uv0) * 0.2 + v_uv0; + gl_FragColor = texture(texture, uv); +} +``` +![demo](./resource/stretch.png) + +这个时候如果我们加上时间参数,我们就可以得到一个往外不停井喷的“黑洞”: + +```c++ +void main() { + vec2 uv = normalize(vec2(0.5, 0.5) - v_uv0) * 0.2 * cc_time.x + v_uv0; + gl_FragColor = texture(texture, uv); +} +``` +![掏空式往外涌](./resource/demo2.gif) + + +但是水波往外扩散是呼吸灯式的一波波往外涌,而且不是这种无尽式的一直把东西往外掏的感觉,所以我们要给`cc_time.x`加上一个周期性的变化,让它能表现出这种周期性的往外扩散的感觉。 + +```c++ +void main() { + vec2 uv = normalize(vec2(0.5, 0.5) - v_uv0) * 0.2 * sin(cc_time.x) + v_uv0; + gl_FragColor = texture(texture, uv); +} +``` +![周期性往外涌](./resource/demo3.gif) + +这种呼吸灯式的涌动其实和我们的最终效果有很大区别,因为它永远在循环涌动,但是我们的水波是从中心扩散出去之后,中间部分就不再动了的,怎么让中间的像素不再多次涌动呢?如果把一圈水波比作圆,那水波的扩散行为其实就是这个圆的半径在不断的增大,圆外面的波纹有效,圆里面的波纹静止。因此我们可以多加一个距离取样,像素离扩散中心的距离大于半径才保留否则丢弃,而这个半径从零开始逐渐增大。 + +```c++ +void main() { + vec2 distance_vec = vec2(0.5, 0.5) - v_uv0; + float sin_factor = sin(cc_time.x) * 0.2; + + float wave_radius = 0.3; + float distance = sqrt(distance_vec.x * distance_vec.x + distance_vec.y * distance_vec.y); + // 其中waveOffset是随时间增长的,通过外部传入 + float dis_factor = clamp(wave_radius - abs(distance - wave_offset), 0.0, 1.0); + + vec2 uv = v_uv0 + normalize(distance_vec) * sin_factor * dis_factor; + gl_FragColor = texture(texture, uv); +} +``` + +![未调优效果](./resource/demo4.gif) + +接下来的就是参数的调试,主要是三角函数的采样那里,我们希望水波能够产生多个波动,所以我们需要乘上一定的倍数,让函数的作用范围足够大,才能有足够多的波峰谷底。另外就是sin函数的输出值域在`(-1, 1)`之间,所以我们的输出也需要缩小一定的倍数,才能让函数的峰值变化处于一个合理的范围。 + +```c++ +void main() { + vec2 distance_vec = center - v_uv0; + distance_vec = distance_vec * vec2(canvas_size.x / canvas_size.y, 1.0); + float distance = sqrt(distance_vec.x * distance_vec.x + distance_vec.y * distance_vec.y); + + // distance小于1,但是我们希望能有多个波峰波谷,所以在sin的内部乘上一个比较大的倍数 + // sin函数的值在-1到1之间,我们希望偏移值很小,所以输出的时候需要缩小一定的倍数倍 + float sin_factor = sin(distance * 100.0 + cc_time.x) * 0.05; + float discard_factor = clamp(wave_radius - abs(wave_offset - distance), 0.0, 1.0); + + // 计算总的uv的偏移值 + vec2 offset = normalize(distance_vec) * sin_factor * discard_factor; + vec2 uv = offset + v_uv0; + + gl_FragColor = texture(texture, uv); +} +``` + +![demo](./resource/demo.gif) + +# 效果预览 + +**源码**获取请点击**查看原文**,长按二维码**查看效果**👇 + +![ewm](./resource/ewm.png) + +我是异名,你的阅读是我的动力 + + + +