使用 opengl 着色器和帧缓冲区时 libgdx 中整个屏幕的爆炸冲击波效果坐标问题
explosion shockwave effect on entire screen in libgdx with opengl shaders and framebuffers trouble with coordinates
我正在尝试在我的 2d 游戏中实现爆炸的冲击波效果
这些是我正在尝试的步骤
1)将所有东西绘制到帧缓冲区(纹理区域和网格)
2)从帧缓冲区获取纹理并垂直翻转
3) 使用 http://empire-defense.crystalin.fr/blog/2d_shock_wave_texture_with_shader
中的着色器将生成的纹理绘制到屏幕上
这是我的渲染方法
resultBuf.begin();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.gl20.glViewport(0, 0, resultBuf.getWidth(), resultBuf.getHeight());
Gdx.gl20.glEnable(GL20.GL_TEXTURE_2D);
batch.begin();
renderbackground();
batch.setProjectionMatrix(camera.combined);
if(imageObjDraw){
for(Integer type:imageObjects.keySet()){
if(type!=ImageObject.mountains&&type!=ImageObject.steelbridge1&&type!=ImageObject.steelbridge2&&type!=ImageObject.steelbridge3)for(ImageObject o:imageObjects.get(type))if(o.isInSquare(camRec))o.draw(batch);
}
}
batch.end();
if (meshDraw) {
//DRAWING MESHES
shaderPassThrough.begin();
Gdx.graphics.getGL20().glEnable(GL20.GL_BLEND);
shaderPassThrough.setUniformMatrix("u_projTrans", camera.combined);
shaderPassThrough.setUniformi("u_texture", 0);
for(Integer key:meshes.keySet()){
if(key!=MeshInfo.bridge){
LinkedList<myMesh> mlist=meshes.get(key);
Texture tex=meshTextures.get(key);
if(tex!=null)tex.bind(0);
for(myMesh m:mlist){
m.render();
}
}
}
shaderPassThrough.end();
}
batch.begin();
if(drawObjEnable){
drawObjects(batch);
vehicle.drawState0(batch);
drawParticles(batch);
}
if(particleDraw)PushTheLimits.animMan.render(batch);
if(bulletEnable)BulletManager.render(batch);
if(imageObjDraw){
for(Integer type:imageObjects.keySet()){
if(type==ImageObject.steelbridge1||type==ImageObject.steelbridge2||type==ImageObject.steelbridge3)for(ImageObject o:imageObjects.get(type))if(o.isInSquare(camRec))o.draw(batch);
}
}
batch.end();
if (meshDraw) {
//DRAWING MESHES
shaderPassThrough.begin();
Gdx.graphics.getGL20().glEnable(GL20.GL_BLEND);
shaderPassThrough.setUniformMatrix("u_projTrans", camera.combined);
shaderPassThrough.setUniformi("u_texture", 0);
for(Integer key:meshes.keySet()){
if(key==MeshInfo.bridge){
LinkedList<myMesh> mlist=meshes.get(key);
Texture tex=meshTextures.get(key);
if(tex!=null)tex.bind(0);
for(myMesh m:mlist){
m.render();
}
}
}
shaderPassThrough.end();
}
resultBuf.end();
batch.begin();
batch.setShader(shaderWave);
shaderWave.begin();
shaderWave.setUniformMatrix("u_projTrans", camera.combined);
shaderWave.setUniformi("sceneTex", 0);
shaderWave.setUniformf("center", new Vector2((explCoorX-camera.position.x+camera.effectivewidth/2f)/camera.zoom/screenW,(explCoorY-camera.position.y+camera.effectiveheight/2f)/camera.zoom/screenH));
shaderWave.setUniformf("time", waveElaps);
shaderWave.setUniformf("shockParams", new Vector3(10.0f, 0.8f, 0.1f));
resultTexReg.setRegion(resultBuf.getColorBufferTexture());
resultTexReg.flip(false, true);
resultTexReg.getTexture().bind();
batch.draw(resultTexReg, camera.position.x-camera.effectivewidth/2f,camera.position.y-camera.effectiveheight/2f,camera.effectivewidth,camera.effectiveheight);
batch.end();
batch.setShader(null);
这里是着色器
片段着色器
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D sceneTex; // 0
uniform vec2 center; // Mouse position
uniform float time; // effect elapsed time
uniform vec3 shockParams; // 10.0, 0.8, 0.1
varying vec2 v_texCoords;
varying vec2 proj;
void main()
{
float distance = distance(v_texCoords, center);
if ( (distance <= (time + shockParams.z)) && (distance >= (time - shockParams.z)) ) {
float diff = (distance - time);
float powDiff = 1.0 - pow(abs(diff*shockParams.x), shockParams.y);
float diffTime = diff * powDiff;
vec2 diffUV = normalize(v_texCoords-center);
v_texCoords = v_texCoords + (diffUV * diffTime);
}
gl_FragColor = texture2D(sceneTex, v_texCoords);
}
顶点着色器
attribute vec4 a_position;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans;
varying vec2 v_texCoords;
varying vec2 proj;
void main() {
//proj=u_projTrans.xy;
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position;
}
关于定义问题
冲击波效果始终适用于屏幕坐标我的意思是我传递给片段着色器的中心变量 - 即 0,0- 必须是我的 box2d 世界坐标的原点但效果始终适用于屏幕的左下角即使我移动相机到地图效果的尽头位于屏幕的左下角,但我希望它位于世界原点 0,0 屏幕的最左下方
我怀疑问题与我在相机位置绘制生成的纹理有关,而且我在着色器中使用相机作为投影矩阵,但我对这个着色器主题和图形坐标没有很好的理解,实际上我研究得很好这看起来对我来说是正确的,但我现在看不到错误并需要帮助
提前致谢 如果我没有把问题说得足够清楚,请发表评论我会编辑并添加所需的信息,甚至你可能想看一些屏幕截图?
您的片段着色器中名为 'center' 的制服未在世界坐标中指定。当您将渲染到纹理块传输到屏幕时,它在纹理坐标中指定。
所以无论你的世界相机在哪里,坐标 0,0 始终是屏幕的左下角,1,1 是右上角。
为了给 vec2 传递一个适当的值 'center',你必须将爆炸的坐标(或任何引起冲击波的东西)从你的世界坐标系 space 转换为纹理 space。
这样做的一种方法是将世界坐标乘以您的相机 WVP 矩阵 (camera.combined)。如果它是透视相机,您可能需要将结果的 xy 除以 w。这应该为您提供从 -1,-1 到 1, 1 的 clip-space 坐标。然后您需要通过加 1 和减半将它们重新映射到纹理坐标。您可以在顶点着色器中执行此操作并通过 varying 传递结果,或者您可以在 CPU 上执行此操作并将结果作为统一传递。
我正在尝试在我的 2d 游戏中实现爆炸的冲击波效果 这些是我正在尝试的步骤 1)将所有东西绘制到帧缓冲区(纹理区域和网格) 2)从帧缓冲区获取纹理并垂直翻转 3) 使用 http://empire-defense.crystalin.fr/blog/2d_shock_wave_texture_with_shader
中的着色器将生成的纹理绘制到屏幕上这是我的渲染方法
resultBuf.begin();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.gl20.glViewport(0, 0, resultBuf.getWidth(), resultBuf.getHeight());
Gdx.gl20.glEnable(GL20.GL_TEXTURE_2D);
batch.begin();
renderbackground();
batch.setProjectionMatrix(camera.combined);
if(imageObjDraw){
for(Integer type:imageObjects.keySet()){
if(type!=ImageObject.mountains&&type!=ImageObject.steelbridge1&&type!=ImageObject.steelbridge2&&type!=ImageObject.steelbridge3)for(ImageObject o:imageObjects.get(type))if(o.isInSquare(camRec))o.draw(batch);
}
}
batch.end();
if (meshDraw) {
//DRAWING MESHES
shaderPassThrough.begin();
Gdx.graphics.getGL20().glEnable(GL20.GL_BLEND);
shaderPassThrough.setUniformMatrix("u_projTrans", camera.combined);
shaderPassThrough.setUniformi("u_texture", 0);
for(Integer key:meshes.keySet()){
if(key!=MeshInfo.bridge){
LinkedList<myMesh> mlist=meshes.get(key);
Texture tex=meshTextures.get(key);
if(tex!=null)tex.bind(0);
for(myMesh m:mlist){
m.render();
}
}
}
shaderPassThrough.end();
}
batch.begin();
if(drawObjEnable){
drawObjects(batch);
vehicle.drawState0(batch);
drawParticles(batch);
}
if(particleDraw)PushTheLimits.animMan.render(batch);
if(bulletEnable)BulletManager.render(batch);
if(imageObjDraw){
for(Integer type:imageObjects.keySet()){
if(type==ImageObject.steelbridge1||type==ImageObject.steelbridge2||type==ImageObject.steelbridge3)for(ImageObject o:imageObjects.get(type))if(o.isInSquare(camRec))o.draw(batch);
}
}
batch.end();
if (meshDraw) {
//DRAWING MESHES
shaderPassThrough.begin();
Gdx.graphics.getGL20().glEnable(GL20.GL_BLEND);
shaderPassThrough.setUniformMatrix("u_projTrans", camera.combined);
shaderPassThrough.setUniformi("u_texture", 0);
for(Integer key:meshes.keySet()){
if(key==MeshInfo.bridge){
LinkedList<myMesh> mlist=meshes.get(key);
Texture tex=meshTextures.get(key);
if(tex!=null)tex.bind(0);
for(myMesh m:mlist){
m.render();
}
}
}
shaderPassThrough.end();
}
resultBuf.end();
batch.begin();
batch.setShader(shaderWave);
shaderWave.begin();
shaderWave.setUniformMatrix("u_projTrans", camera.combined);
shaderWave.setUniformi("sceneTex", 0);
shaderWave.setUniformf("center", new Vector2((explCoorX-camera.position.x+camera.effectivewidth/2f)/camera.zoom/screenW,(explCoorY-camera.position.y+camera.effectiveheight/2f)/camera.zoom/screenH));
shaderWave.setUniformf("time", waveElaps);
shaderWave.setUniformf("shockParams", new Vector3(10.0f, 0.8f, 0.1f));
resultTexReg.setRegion(resultBuf.getColorBufferTexture());
resultTexReg.flip(false, true);
resultTexReg.getTexture().bind();
batch.draw(resultTexReg, camera.position.x-camera.effectivewidth/2f,camera.position.y-camera.effectiveheight/2f,camera.effectivewidth,camera.effectiveheight);
batch.end();
batch.setShader(null);
这里是着色器 片段着色器
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D sceneTex; // 0
uniform vec2 center; // Mouse position
uniform float time; // effect elapsed time
uniform vec3 shockParams; // 10.0, 0.8, 0.1
varying vec2 v_texCoords;
varying vec2 proj;
void main()
{
float distance = distance(v_texCoords, center);
if ( (distance <= (time + shockParams.z)) && (distance >= (time - shockParams.z)) ) {
float diff = (distance - time);
float powDiff = 1.0 - pow(abs(diff*shockParams.x), shockParams.y);
float diffTime = diff * powDiff;
vec2 diffUV = normalize(v_texCoords-center);
v_texCoords = v_texCoords + (diffUV * diffTime);
}
gl_FragColor = texture2D(sceneTex, v_texCoords);
}
顶点着色器
attribute vec4 a_position;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans;
varying vec2 v_texCoords;
varying vec2 proj;
void main() {
//proj=u_projTrans.xy;
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position;
}
关于定义问题 冲击波效果始终适用于屏幕坐标我的意思是我传递给片段着色器的中心变量 - 即 0,0- 必须是我的 box2d 世界坐标的原点但效果始终适用于屏幕的左下角即使我移动相机到地图效果的尽头位于屏幕的左下角,但我希望它位于世界原点 0,0 屏幕的最左下方 我怀疑问题与我在相机位置绘制生成的纹理有关,而且我在着色器中使用相机作为投影矩阵,但我对这个着色器主题和图形坐标没有很好的理解,实际上我研究得很好这看起来对我来说是正确的,但我现在看不到错误并需要帮助 提前致谢 如果我没有把问题说得足够清楚,请发表评论我会编辑并添加所需的信息,甚至你可能想看一些屏幕截图?
您的片段着色器中名为 'center' 的制服未在世界坐标中指定。当您将渲染到纹理块传输到屏幕时,它在纹理坐标中指定。
所以无论你的世界相机在哪里,坐标 0,0 始终是屏幕的左下角,1,1 是右上角。
为了给 vec2 传递一个适当的值 'center',你必须将爆炸的坐标(或任何引起冲击波的东西)从你的世界坐标系 space 转换为纹理 space。
这样做的一种方法是将世界坐标乘以您的相机 WVP 矩阵 (camera.combined)。如果它是透视相机,您可能需要将结果的 xy 除以 w。这应该为您提供从 -1,-1 到 1, 1 的 clip-space 坐标。然后您需要通过加 1 和减半将它们重新映射到纹理坐标。您可以在顶点着色器中执行此操作并通过 varying 传递结果,或者您可以在 CPU 上执行此操作并将结果作为统一传递。