我在 Phaser 游戏中遇到着色器问题。我已将着色器应用为后期管道,希望它能够以水效果覆盖整个屏幕。但当我移动角色时,效果也会移动。这就像着色器粘在相机上而不是留在屏幕上。
我整理了一个快速演示,以便您明白我的意思。您可以使用箭头键四处移动并检查问题:https://phaser.io/sandbox/vFDT9xXy
这是相关的着色器代码:
const waterFragShader = `
#ifdef GL_ES
precision mediump float;
#endif
uniform float uTime;
uniform vec2 uResolution;
uniform sampler2D uMainSampler;
varying vec2 outTexCoord;
void main(void)
{
vec2 uv = gl_FragCoord.xy / uResolution.xy;
vec2 distortion = vec2(
sin(uv.y * 10.0 + uTime) * 0.002,
cos(uv.x * 10.0 + uTime) * 0.002
);
vec4 texture_color = texture2D(uMainSampler, uv + distortion);
vec4 k = vec4(uTime)*0.6;
k.xy = uv * 7.0;
float val1 = length(0.5-fract(k.xyw*=mat3(vec3(-2.0,-1.0,0.0), vec3(3.0,-1.0,1.0), vec3(1.0,-1.0,-1.0))*0.5));
float val2 = length(0.5-fract(k.xyw*=mat3(vec3(-2.0,-1.0,0.0), vec3(3.0,-1.0,1.0), vec3(1.0,-1.0,-1.0))*0.2));
float val3 = length(0.5-fract(k.xyw*=mat3(vec3(-2.0,-1.0,0.0), vec3(3.0,-1.0,1.0), vec3(1.0,-1.0,-1.0))*0.5));
float pattern = pow(min(min(val1,val2),val3), 7.0) * 2.0;
vec4 pattern_color = vec4(1, 1, 1, pattern);
vec4 color = vec4(pattern, pattern, pattern, pattern);
gl_FragColor = mix(texture_color, pattern_color, pattern_color.a);
}
`;
class WaterPipeline extends Phaser.Renderer.WebGL.Pipelines
.PostFXPipeline {
constructor(game) {
super({
game,
name: "Water",
fragShader: waterFragShader,
});
}
onPreRender() {
this.set1f("uTime", this.game.loop.time / 1000);
}
onDraw(renderTarget) {
this.set2f("uResolution", renderTarget.width, renderTarget.height);
this.bindAndDraw(renderTarget);
}
}
关于如何让这个着色器保持原样并覆盖整个屏幕,无论相机移动到哪里,有什么想法吗?
我不是着色器专家,所以请对我的回答持保留态度。我最初会说不,这实际上不可能,因为失真是由当前图像生成的。
但是我记得你可以将变量传递到着色器中,所以如果你将玩家的位置作为偏移传递,扭曲似乎会移动。
简短演示:
(显示玩家偏移量;使用箭头键移动玩家=红色方块)
const waterFragShader = `
#ifdef GL_ES
precision mediump float;
#endif
uniform float uTime;
uniform vec2 uResolution;
uniform sampler2D uMainSampler;
varying vec2 outTexCoord;
// ADDED the OffSet Variable
uniform vec2 offSet;
void main(void)
{
vec2 uv = (gl_FragCoord.xy / uResolution.xy) + offSet ;
vec2 distortion = vec2(
sin(uv.y * 10.0 + uTime) * 0.002,
cos(uv.x * 10.0 + uTime) * 0.002
);
vec4 texture_color = texture2D(uMainSampler, uv + distortion);
vec4 k = vec4(uTime)*0.6;
k.xy = uv * 7.0;
float val1 = length(0.5-fract(k.xyw*=mat3(vec3(-2.0,-1.0,0.0), vec3(3.0,-1.0,1.0), vec3(1.0,-1.0,-1.0))*0.5));
float val2 = length(0.5-fract(k.xyw*=mat3(vec3(-2.0,-1.0,0.0), vec3(3.0,-1.0,1.0), vec3(1.0,-1.0,-1.0))*0.2));
float val3 = length(0.5-fract(k.xyw*=mat3(vec3(-2.0,-1.0,0.0), vec3(3.0,-1.0,1.0), vec3(1.0,-1.0,-1.0))*0.5));
float pattern = pow(min(min(val1,val2),val3), 7.0) * 2.0;
vec4 pattern_color = vec4(1, 1, 1, pattern + .2); // added .2 to make it more visible
vec4 color = vec4(pattern, pattern, pattern, pattern);
gl_FragColor = mix(texture_color, pattern_color, pattern_color.a);
}
`;
class WaterPipeline extends Phaser.Renderer.WebGL.Pipelines
.PostFXPipeline {
constructor(game) {
super({
game,
name: "Water",
fragShader: waterFragShader,
});
// Initialize the OffSet Variable
this.offset = { x: 10, y: 10 };
}
setOffset(x,y){
// Update OffSet Variable
this.offset = { x, y};
}
onPreRender() {
this.set1f("uTime", this.game.loop.time / 1000);
// pass OffSet Variable to shader
this.set2f("offSet", this.offset.x / 1000, this.offset.y/ 1000);
}
onDraw(renderTarget) {
this.set2f("uResolution", renderTarget.width, renderTarget.height);
this.bindAndDraw(renderTarget);
}
}
class MainScene extends Phaser.Scene {
constructor() {
super({ key: "MainScene" });
}
create() {
this.renderer.pipelines.addPostPipeline("Water", WaterPipeline);
this.bg = this.add.rectangle(100, 100, 100, 100, 0x0000ff)
.setPostPipeline("Water");
this.player = this.add
.rectangle(200, 100, 10, 10, 0xff0000)
.setOrigin(0, 0);
this.cameras.main.startFollow(this.player)
}
update() {
const cursors = this.input.keyboard.createCursorKeys();
if (cursors.left.isDown) {
this.player.x -= 10;
} else if (cursors.right.isDown) {
this.player.x += 10;
} else if (cursors.up.isDown) {
this.player.y -= 10;
} else if (cursors.down.isDown) {
this.player.y += 10;
}
// Pass the players position
this.bg.getPostPipeline("Water").setOffset(this.player.x, this.player.y)
}
}
var config = {
width: 540,
height: 180,
scene: [MainScene]
};
new Phaser.Game(config);
console.clear();
document.body.style = 'margin:0;';
<script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>
可能有更好的方法,和/或着色器代码可能/将需要更多调整,但这是主要思想。