Libgdx - 将着色器映射到世界
Libgdx - map shader to world
我有一个 2d 关卡,box2d 负责物理。我已经实现了本教程中描述的以下 water effect。我正在使用一个以主角精灵为中心的 OrthographicCamera。相机跟随精灵,当它到达关卡的边缘时,相机停止移动。目前着色器只渲染相机底部的水。我的问题是我将如何沿着关卡底部渲染水,以便当精灵从关卡底部移开时水 "moves" 离开屏幕?
public class GameRenderer {
private ImageProvider imageProvider;
private GameWorld world;
private SpriteBatch spriteBatch;
private OrthographicCamera camera;
Vector2 camPos;
private Player player;
private Sprite itemPointer;
private Sprite playerSprite;
Sprite explosion;
Box2DDebugRenderer debugRenderer = new Box2DDebugRenderer();
private Array<SimpleSpatial> mSpatials; // used for rendering rube images
private static final Vector2 mTmp = new Vector2();
private Map<String, Texture> mTextureMap;
private Map<Texture, TextureRegion> mTextureRegionMap;
//From tutorial
String vertexShader =
"attribute vec4 a_position; \n"
+ "attribute vec2 a_texCoord0;\n"
+ "uniform mat4 u_worldView;\n"
+ "varying vec4 v_color;"
+ "varying vec2 v_texCoords;"
+ "void main() \n"
+ "{ \n"
+ " v_color = vec4(1, 1, 1, 1); \n"
+ " v_texCoords = a_texCoord0; \n"
+ " gl_Position = u_worldView * a_position; \n"
+ "} \n";
String fragmentShader = "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n"
+ "varying vec4 v_color;\n"
+ "varying vec2 v_texCoords;\n"
+ "uniform sampler2D u_texture;\n"
+ "uniform sampler2D u_texture2;\n"
+ "uniform float timedelta;\n"
+ "void main() \n"
+ "{ \n"
+ " vec2 displacement = texture2D(u_texture2, v_texCoords/6.0).xy;\n" //
+ " float t=v_texCoords.y +displacement.y*0.1-0.1+ (sin(v_texCoords.x * 60.0+timedelta) * 0.005); \n" //
+ " gl_FragColor = v_color * texture2D(u_texture, vec2(v_texCoords.x,t));\n"
+ "}";
String fragmentShader2 = "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n"
+ "varying vec4 v_color;\n"
+ "varying vec2 v_texCoords;\n"
+ "uniform sampler2D u_texture;\n"
+ "void main() \n"
+ "{ \n"
+ " gl_FragColor = v_color * texture2D(u_texture, v_texCoords);\n"
+ "}";
ShaderProgram shader;
ShaderProgram waterShader;
Matrix4 matrix;
float time;
Mesh waterMesh;
private Texture texture2;
private Texture texture3;
FPSLogger fpsLogger = new FPSLogger();
public GameRenderer(GameWorld gameWorld) {
imageProvider = GameManager.getInstance().getImageProvider();
world = gameWorld;
spriteBatch = new SpriteBatch();
camPos = new Vector2();
loadBackgroundTextures();
loadPlayerTextures();
explosion = new Sprite(imageProvider.getExplosion());
explosion.setScale(50 * Constants.WORLD_TO_BOX);
setupCamera();
Gdx.input.setCatchBackKey(true);
}
public void show() {
initGame();
}
private void initGame() {
player = world.getPlayer();
playerSprite = new Sprite(playerTexture);
playerSprite.setScale(15 * Constants.WORLD_TO_BOX, 15 * Constants.WORLD_TO_BOX);
createSpatialsFromRubeImages(world.getScene());
initialiseShaders();
}
private void createSpatialsFromRubeImages(RubeScene scene)
{
Array<RubeImage> images = scene.getImages();
if ((images != null) && (images.size > 0))
{
mSpatials = new Array<>();
for (int i = 0; i < images.size; i++)
{
RubeImage image = images.get(i);
mTmp.set(image.width, image.height);
String textureFileName = image.file;
Texture texture = mTextureMap.get(textureFileName);
if (texture == null)
{
texture = new Texture(textureFileName);
mTextureMap.put(textureFileName, texture);
}
SimpleSpatial spatial = new SimpleSpatial(texture, image.flip, image.body, image.color, mTmp, image.center,
image.angleInRads * MathUtils.radiansToDegrees);
mSpatials.add(spatial);
}
}
}
//From tutorial
public void initialiseShaders() {
texture2 =new Texture(Gdx.files.internal("gfx/shaders/water.png"));
texture2.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
texture3 = new Texture(Gdx.files.internal("gfx/shaders/waterdisplacement.png"));
texture3.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
texture3.bind();
matrix = new Matrix4();
ShaderProgram.pedantic=false;
shader = new ShaderProgram(vertexShader, fragmentShader);
waterShader = new ShaderProgram(vertexShader, fragmentShader2);
waterShader.setUniformMatrix("u_projTrans", matrix);
waterMesh = createQuad();
time=1f;
}
//From tutorial
public Mesh createQuad() {
float[] verts = new float[20];
int i = 0;
verts[i++] = -1; // x1
verts[i++] = -1; // y1
verts[i++] = 0;
verts[i++] = 1f; // u1
verts[i++] = 1f; // v1 //
verts[i++] = 1f; // x2
verts[i++] = -1; // y2
verts[i++] = 0;
verts[i++] = 0f; // u2
verts[i++] = 1f; // v2 //
verts[i++] = 1; // x3
verts[i++] = -0.3f; // y2
verts[i++] = 0;
verts[i++] = 0f; // u3
verts[i++] = 0f; // v3
verts[i++] = -1; // x4
verts[i++] = -0.3f; // y4
verts[i++] = 0;
verts[i++] = 1f; // u4
verts[i++] = 0f; // v4
Mesh mesh = new Mesh(true, 4, 0, // static mesh with 4 vertices and no
// indices
new VertexAttribute(VertexAttributes.Usage.Position, 3,
ShaderProgram.POSITION_ATTRIBUTE), new VertexAttribute(
VertexAttributes.Usage.TextureCoordinates, 2,
ShaderProgram.TEXCOORD_ATTRIBUTE + "0"));
mesh.setVertices(verts);
return mesh;
}
private void setupCamera() {
camera = new OrthographicCamera(imageProvider.getScreenWidth(), imageProvider.getScreenHeight());
camera.position.x = 500;
camera.position.y = 500;
camera.zoom = Constants.ZOOM_FACTOR;
camera.update();
}
public void render(float delta) {
spriteBatch.flush();
Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);
fpsLogger.log();
Gdx.gl.glScissor(0, 0, (int)Constants.SCREEN_WIDTH, (int)Constants.SCREEN_HEIGHT);
Gdx.gl.glDisable(GL20.GL_SCISSOR_TEST);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.gl.glEnable(GL20.GL_SCISSOR_TEST);
camera.update();
spriteBatch.setProjectionMatrix(camera.combined);
drawBackground();
updateCameraPosition();
drawRest(delta);
drawPlayer(delta);
//Render water shader
drawWater();
debugRenderer.render(world.getRubeWorld(), camera.combined);
}
public void drawWater() {
spriteBatch.setProjectionMatrix(camera.combined);
float dt = Gdx.graphics.getDeltaTime();
time += dt;
float angle = time * (2 * MathUtils.PI);
if (angle > (2 * MathUtils.PI))
angle -= (2 * MathUtils.PI);
//RENDER WATER
Gdx.gl20.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl20.glEnable(GL20.GL_BLEND);
texture2.bind(1);
texture3.bind(2);
shader.begin();
shader.setUniformMatrix("u_worldView", matrix);
shader.setUniformi("u_texture", 1);
shader.setUniformi("u_texture2", 2);
shader.setUniformf("timedelta", -angle);
shader.setUniformf("v_texCoords.y", -10f );
waterMesh.render(shader, GL20.GL_TRIANGLE_FAN);
shader.end();
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0);
//Gdx.gl20.glDisable(GL20.GL_BLEND);
}
public void drawBackground() {
spriteBatch.begin();
spriteBatch.setProjectionMatrix(parallaxCamera.combined);
spriteBatch.draw(backgroundImage, parallaxCamera.position.x - 400, parallaxCamera.position.y - 240);
spriteBatch.end();
}
public void drawRest(float delta) {
camera.update();
spriteBatch.setProjectionMatrix(camera.combined);
spriteBatch.begin();
//Renders Box2d world
for (int i = 0; i < mSpatials.size; i++)
{
mSpatials.get(i).render(spriteBatch, 0);
}
spriteBatch.end();
}
public void drawPlayer(float delta) {
if (!world.isGameOver()) {
spriteBatch.begin();
playerSprite.setOrigin(playerSprite.getWidth() / 2, playerSprite.getHeight() / 2);
playerSprite.setPosition(world.getPlayer().getPosition().x - playerSprite.getWidth() / 2, world.getPlayer().getPosition().y - playerSprite.getHeight() / 2);
playerSprite.setRotation(world.getPlayer().getDirection() - 90);
playerSprite.draw(spriteBatch);
spriteBatch.end();
}
else {
spriteBatch.begin();
explosion.draw(spriteBatch);
spriteBatch.end();
}
}
private void updateCameraPosition() {
camPos.lerp(world.getPlayer().getPosition(), 1.5f);
if (camPos.x * Constants.WORLD_TO_BOX < LEVELWIDTH && camPos.x * Constants.WORLD_TO_BOX > 0) {
camera.position.x = camPos.x;
}
if (camPos.y * Constants.WORLD_TO_BOX < LEVELHEIGHT && camPos.y * Constants.WORLD_TO_BOX > 0.75) {
camera.position.y = camPos.y;
}
}
}
您是否将 camera.combined 矩阵发送到 u_worldView?
shader.setUniformMatrix("u_worldView", cam.combined);
如果您有一些代码片段会很有帮助。
我有一个 2d 关卡,box2d 负责物理。我已经实现了本教程中描述的以下 water effect。我正在使用一个以主角精灵为中心的 OrthographicCamera。相机跟随精灵,当它到达关卡的边缘时,相机停止移动。目前着色器只渲染相机底部的水。我的问题是我将如何沿着关卡底部渲染水,以便当精灵从关卡底部移开时水 "moves" 离开屏幕?
public class GameRenderer {
private ImageProvider imageProvider;
private GameWorld world;
private SpriteBatch spriteBatch;
private OrthographicCamera camera;
Vector2 camPos;
private Player player;
private Sprite itemPointer;
private Sprite playerSprite;
Sprite explosion;
Box2DDebugRenderer debugRenderer = new Box2DDebugRenderer();
private Array<SimpleSpatial> mSpatials; // used for rendering rube images
private static final Vector2 mTmp = new Vector2();
private Map<String, Texture> mTextureMap;
private Map<Texture, TextureRegion> mTextureRegionMap;
//From tutorial
String vertexShader =
"attribute vec4 a_position; \n"
+ "attribute vec2 a_texCoord0;\n"
+ "uniform mat4 u_worldView;\n"
+ "varying vec4 v_color;"
+ "varying vec2 v_texCoords;"
+ "void main() \n"
+ "{ \n"
+ " v_color = vec4(1, 1, 1, 1); \n"
+ " v_texCoords = a_texCoord0; \n"
+ " gl_Position = u_worldView * a_position; \n"
+ "} \n";
String fragmentShader = "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n"
+ "varying vec4 v_color;\n"
+ "varying vec2 v_texCoords;\n"
+ "uniform sampler2D u_texture;\n"
+ "uniform sampler2D u_texture2;\n"
+ "uniform float timedelta;\n"
+ "void main() \n"
+ "{ \n"
+ " vec2 displacement = texture2D(u_texture2, v_texCoords/6.0).xy;\n" //
+ " float t=v_texCoords.y +displacement.y*0.1-0.1+ (sin(v_texCoords.x * 60.0+timedelta) * 0.005); \n" //
+ " gl_FragColor = v_color * texture2D(u_texture, vec2(v_texCoords.x,t));\n"
+ "}";
String fragmentShader2 = "#ifdef GL_ES\n"
+ "precision mediump float;\n"
+ "#endif\n"
+ "varying vec4 v_color;\n"
+ "varying vec2 v_texCoords;\n"
+ "uniform sampler2D u_texture;\n"
+ "void main() \n"
+ "{ \n"
+ " gl_FragColor = v_color * texture2D(u_texture, v_texCoords);\n"
+ "}";
ShaderProgram shader;
ShaderProgram waterShader;
Matrix4 matrix;
float time;
Mesh waterMesh;
private Texture texture2;
private Texture texture3;
FPSLogger fpsLogger = new FPSLogger();
public GameRenderer(GameWorld gameWorld) {
imageProvider = GameManager.getInstance().getImageProvider();
world = gameWorld;
spriteBatch = new SpriteBatch();
camPos = new Vector2();
loadBackgroundTextures();
loadPlayerTextures();
explosion = new Sprite(imageProvider.getExplosion());
explosion.setScale(50 * Constants.WORLD_TO_BOX);
setupCamera();
Gdx.input.setCatchBackKey(true);
}
public void show() {
initGame();
}
private void initGame() {
player = world.getPlayer();
playerSprite = new Sprite(playerTexture);
playerSprite.setScale(15 * Constants.WORLD_TO_BOX, 15 * Constants.WORLD_TO_BOX);
createSpatialsFromRubeImages(world.getScene());
initialiseShaders();
}
private void createSpatialsFromRubeImages(RubeScene scene)
{
Array<RubeImage> images = scene.getImages();
if ((images != null) && (images.size > 0))
{
mSpatials = new Array<>();
for (int i = 0; i < images.size; i++)
{
RubeImage image = images.get(i);
mTmp.set(image.width, image.height);
String textureFileName = image.file;
Texture texture = mTextureMap.get(textureFileName);
if (texture == null)
{
texture = new Texture(textureFileName);
mTextureMap.put(textureFileName, texture);
}
SimpleSpatial spatial = new SimpleSpatial(texture, image.flip, image.body, image.color, mTmp, image.center,
image.angleInRads * MathUtils.radiansToDegrees);
mSpatials.add(spatial);
}
}
}
//From tutorial
public void initialiseShaders() {
texture2 =new Texture(Gdx.files.internal("gfx/shaders/water.png"));
texture2.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
texture3 = new Texture(Gdx.files.internal("gfx/shaders/waterdisplacement.png"));
texture3.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
texture3.bind();
matrix = new Matrix4();
ShaderProgram.pedantic=false;
shader = new ShaderProgram(vertexShader, fragmentShader);
waterShader = new ShaderProgram(vertexShader, fragmentShader2);
waterShader.setUniformMatrix("u_projTrans", matrix);
waterMesh = createQuad();
time=1f;
}
//From tutorial
public Mesh createQuad() {
float[] verts = new float[20];
int i = 0;
verts[i++] = -1; // x1
verts[i++] = -1; // y1
verts[i++] = 0;
verts[i++] = 1f; // u1
verts[i++] = 1f; // v1 //
verts[i++] = 1f; // x2
verts[i++] = -1; // y2
verts[i++] = 0;
verts[i++] = 0f; // u2
verts[i++] = 1f; // v2 //
verts[i++] = 1; // x3
verts[i++] = -0.3f; // y2
verts[i++] = 0;
verts[i++] = 0f; // u3
verts[i++] = 0f; // v3
verts[i++] = -1; // x4
verts[i++] = -0.3f; // y4
verts[i++] = 0;
verts[i++] = 1f; // u4
verts[i++] = 0f; // v4
Mesh mesh = new Mesh(true, 4, 0, // static mesh with 4 vertices and no
// indices
new VertexAttribute(VertexAttributes.Usage.Position, 3,
ShaderProgram.POSITION_ATTRIBUTE), new VertexAttribute(
VertexAttributes.Usage.TextureCoordinates, 2,
ShaderProgram.TEXCOORD_ATTRIBUTE + "0"));
mesh.setVertices(verts);
return mesh;
}
private void setupCamera() {
camera = new OrthographicCamera(imageProvider.getScreenWidth(), imageProvider.getScreenHeight());
camera.position.x = 500;
camera.position.y = 500;
camera.zoom = Constants.ZOOM_FACTOR;
camera.update();
}
public void render(float delta) {
spriteBatch.flush();
Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);
fpsLogger.log();
Gdx.gl.glScissor(0, 0, (int)Constants.SCREEN_WIDTH, (int)Constants.SCREEN_HEIGHT);
Gdx.gl.glDisable(GL20.GL_SCISSOR_TEST);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.gl.glEnable(GL20.GL_SCISSOR_TEST);
camera.update();
spriteBatch.setProjectionMatrix(camera.combined);
drawBackground();
updateCameraPosition();
drawRest(delta);
drawPlayer(delta);
//Render water shader
drawWater();
debugRenderer.render(world.getRubeWorld(), camera.combined);
}
public void drawWater() {
spriteBatch.setProjectionMatrix(camera.combined);
float dt = Gdx.graphics.getDeltaTime();
time += dt;
float angle = time * (2 * MathUtils.PI);
if (angle > (2 * MathUtils.PI))
angle -= (2 * MathUtils.PI);
//RENDER WATER
Gdx.gl20.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl20.glEnable(GL20.GL_BLEND);
texture2.bind(1);
texture3.bind(2);
shader.begin();
shader.setUniformMatrix("u_worldView", matrix);
shader.setUniformi("u_texture", 1);
shader.setUniformi("u_texture2", 2);
shader.setUniformf("timedelta", -angle);
shader.setUniformf("v_texCoords.y", -10f );
waterMesh.render(shader, GL20.GL_TRIANGLE_FAN);
shader.end();
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0);
//Gdx.gl20.glDisable(GL20.GL_BLEND);
}
public void drawBackground() {
spriteBatch.begin();
spriteBatch.setProjectionMatrix(parallaxCamera.combined);
spriteBatch.draw(backgroundImage, parallaxCamera.position.x - 400, parallaxCamera.position.y - 240);
spriteBatch.end();
}
public void drawRest(float delta) {
camera.update();
spriteBatch.setProjectionMatrix(camera.combined);
spriteBatch.begin();
//Renders Box2d world
for (int i = 0; i < mSpatials.size; i++)
{
mSpatials.get(i).render(spriteBatch, 0);
}
spriteBatch.end();
}
public void drawPlayer(float delta) {
if (!world.isGameOver()) {
spriteBatch.begin();
playerSprite.setOrigin(playerSprite.getWidth() / 2, playerSprite.getHeight() / 2);
playerSprite.setPosition(world.getPlayer().getPosition().x - playerSprite.getWidth() / 2, world.getPlayer().getPosition().y - playerSprite.getHeight() / 2);
playerSprite.setRotation(world.getPlayer().getDirection() - 90);
playerSprite.draw(spriteBatch);
spriteBatch.end();
}
else {
spriteBatch.begin();
explosion.draw(spriteBatch);
spriteBatch.end();
}
}
private void updateCameraPosition() {
camPos.lerp(world.getPlayer().getPosition(), 1.5f);
if (camPos.x * Constants.WORLD_TO_BOX < LEVELWIDTH && camPos.x * Constants.WORLD_TO_BOX > 0) {
camera.position.x = camPos.x;
}
if (camPos.y * Constants.WORLD_TO_BOX < LEVELHEIGHT && camPos.y * Constants.WORLD_TO_BOX > 0.75) {
camera.position.y = camPos.y;
}
}
}
您是否将 camera.combined 矩阵发送到 u_worldView?
shader.setUniformMatrix("u_worldView", cam.combined);
如果您有一些代码片段会很有帮助。