I want to use a simple shader in my 2D-libgdx-project, using OpenGL ES. I've never used OpenGL ES before, so maybe my headers are not right?
Vertex-Shader:
in vec3 position;
in vec2 textureCoords;
out vec2 passTextureCoords;
void main(void)
{
gl_Position = vec4(position.x, position.y, position.z, 1.0);
passTextureCoords = textureCoords;
}
Fragment-Shader:
#ifdef GL_ES
precision mediump float;
#endif
in vec2 passTextureCoords;
out vec4 outColour;
uniform sampler2D textureSampler;
void main(void)
{
// Get color at coordinate by sampler and output it
// outColour = texture(textureSampler, passTextureCoords);
// gl_FragColor = vec4(1.0,0.0,0.0,1.0);
outColour = vec4(1.0, 0.0, 0.0, 1.0);
}
Actually I've used OpenGL 3+ quite often in the past, but never alongside Libgdx and never OpenGL ES.
What this shader should do: Take each pixel and return red instead. But all I get is white.
I create it like this:
private ShaderProgram shader;
this.shader = new ShaderProgram(Gdx.files.internal("shaders/test.vertex").readString(), Gdx.files.internal("shaders/test.fragment").readString());
this.shader.pedantic = false;
and render like that:
public void render(SpriteBatch spriteBatch)
{
spriteBatch.setShader(this.shader);
model.render(spriteBatch);
spriteBatch.setShader(null);
outline.render(spriteBatch);
}
All I get is white for the model, not red. So I guess something is wrong. I do not want to specify any more uniforms, so I've set pedantic to false. What am I doing wrong?
Libgdx still uses OpenGL ES 2.0, so the shaders need different keywords. (This was changed in OpenGL ES 3.0 so it more closely matches OpenGL 3.x, but Libgdx doesn't fully support that yet.)
Replace keyword in in the vertex shader with attribute.
Replace keyword out in the vertex shader with varying.
Replace keyword in in the fragment shader with varying.
Remove outColour and use the built-in gl_FragColor
Additionally, you have to use the attribute names that SpriteBatch is expecting so your attributes must be a_position, a_texCoord0, and if you need it a_color. And likewise your uniform has to be what SpriteBatch is using so change textureSampler to u_texture.
And I saw you calling the function texture in your commented out code, but in OpenGL ES 2.0, you need to use the function texture2D instead.
Related
I want to set boolean value in my fragment shader beacuse i need to recognize when to color objects by multiplying. but i dont know how to acess this boolean variable textured from java code. Project is created in java for android, using opengl es 3.0.
precision mediump float;
uniform vec4 vColor;
uniform bool textured;
uniform sampler2D uTexture;
varying vec2 vTexCoordinate;
void main(){
if(textured){
gl_FragColor = texture2D(uTexture, vTexCoordinate);
gl_FragColor *= vColor;
} else {
gl_FragColor = vColor;
}
}
I recommend to use an int rather than a bool: if (textured != 0)
Alternatively you can pass a floating point value which weights the texture:
precision mediump float;
uniform vec4 vColor;
uniform float textured;
uniform sampler2D uTexture;
varying vec2 vTexCoordinate;
void main()
{
gl_FragColor =
vColor * mix(vec4(1.0), texture2D(uTexture, vTexCoordinate), textured);
}
mix linearly interpolate between two values. Look at the expression:
gl_FragColor =
vColor * mix(vec4(1.0), texture2D(uTexture, vTexCoordinate), textured);
If textured == 0.0, the color is multiplied by vec4(1.0):
gl_FragColor = vColor * vec4(1.0);
If textured == 1.0, then the color is multiplied by the color returned from the texture lookup:
gl_FragColor = vColor * texture2D(uTexture, vTexCoordinate);
If 0.0 < textured < 1.0, then vec4(1.0) and the texture color are interpolated linearly.
Furthermore be careful when you look up a texture in a condition statement. See OpenGL ES 1.1 Full Specification - 6 Texture Accesses; page 110:
Accessing mip-mapped textures within the body of a non-uniform conditional block gives an undefined value. A non-uniform conditional block is a block whose execution cannot be determined at compile time.
I have a problem, if I try to render a textured quad, nothing appear on the screen.
A simple colored quad works great.
It seems that only if I use the "texture2D" function nothing works.
But I am not getting any error message if I compile the shaders or link the program.
This work (A red quad appear on the screen):
uniform sampler2D un_TextureUnit;
varying vec2 fr_UV;
void main()
{
gl_FragColor = vec4(1, 0, 0, 1);
}
This does not work (Nothing appear on the screen):
uniform sampler2D un_TextureUnit;
varying vec2 fr_UV;
void main()
{
gl_FragColor = texture2D(un_TextureUnit, fr_UV);
}
This does not work (Nothing appear on the screen, too):
uniform sampler2D un_TextureUnit;
varying vec2 fr_UV;
void main()
{
vec4 c = texture2D(un_TextureUnit, vec2(0.5, 0.5));
gl_FragColor = vec4(1, 0, 0, 1);
}
Note that I only changed to shader and nothing on the code.
Only If I use the "texture2D" function the shader seems not to compile, but I dont get any error message.
My fragment shader works fine to draw texture on triangles:
precision highp float;
uniform sampler2D u_texture0;
varying vec2 v_textureCoordinate0;
void main()
{
gl_FragColor = texture2D(u_texture0, v_textureCoordinate0);
}
So, i believe the problem isn't in your fragment shader (that is similar to mine), but in the code to prepare shader. I suggest you to check:
if your code to bind texture is correct (check GLES20.glVertexAttribPointer, GLES20.glEnableVertexAttribArray)
if there is any face culling
Anyway, here there is a good tutorial to how to draw a triangle with texture.
So I'm working with Java/LibGDX and I'm trying to set up the very basics of deferred rendering, namely rendering the actual game art to one color buffer of an FBO and the corresponding normals to another color buffer of the FBO.
(So essentially, I'm wanting to create this and this by using multiple-render-targets.)
My problem is my end output is blank, as if nothing is being rendered or is being rendered incorrectly.
My shader (vertex and fragment)
I'm sort of sure this works, since if I just render some sprites completely normally with it enabled to the screen (not an FBO), they do render.
#version 150
in vec4 a_position;
in vec4 a_color;
in vec2 a_texCoord0;
uniform mat4 u_projTrans;
out vec4 v_color;
out vec2 v_texCoords;
void main()
{
v_color = a_color;
v_color.a = v_color.a * (255.0/254.0);
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position;
}
#version 150
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
in LOWP vec4 v_color;
in vec2 v_texCoords;
uniform sampler2D u_texture;
uniform sampler2D u_normal;
out vec4 fragColor;
void main()
{
gl_FragData[0] = v_color * texture(u_texture, v_texCoords);
gl_FragData[1] = texture(u_normal, v_texCoords);
}
The following code is in the render loop.
The basic idea is I'm doing binding the two color buffers of the FBO with glDrawBuffers. I then bind two texture units, game art and normal'd game art. My shader above is supposed to take this and output game art to one color buffer and the corresponding normal art to the other.
// Position the camera.
_cameraRef.position.set(_stage.getWidth() * 0.5f, _stage.getHeight() * 0.5f, 0);
// Update the camera, SpriteBatch, and map renderer.
_cameraRef.update();
_spriteBatch.setProjectionMatrix(_cameraRef.combined);
_mapRenderer.setView(_cameraRef);
// Bind the color texture units of the FBO (multiple-render-targets).
Gdx.gl30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, _gBufferFBOHandle);
IntBuffer intBuffer = BufferUtils.newIntBuffer(2);
intBuffer.put(GL30.GL_COLOR_ATTACHMENT0);
intBuffer.put(GL30.GL_COLOR_ATTACHMENT1);
Gdx.gl30.glDrawBuffers(2, intBuffer);
// Draw!
Gdx.gl30.glClearColor(0, 0, 0, 1);
Gdx.gl30.glClear(GL30.GL_COLOR_BUFFER_BIT);
_spriteBatch.setShader(_shaderGBuffer);
_spriteBatch.begin();
_tilesAtlasNormal.bind(1); // Using multiple texture units to draw art and normals at same time to the two color buffers of the FBO.
_tilesAtlas.bind(0);
_mapRenderer.renderTileLayer(_mapLayerBackground);
_spriteBatch.end();
_spriteBatch.setShader(null);
// Bind the default FBO.
Gdx.gl30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
// Draw the contents of the FBO onto the screen to see what it looks like.
Gdx.gl30.glClearColor(0, 0, 0, 1);
Gdx.gl30.glClear(GL30.GL_COLOR_BUFFER_BIT);
_spriteBatch.begin();
_spriteBatch.draw(_gBufferTexture1, 0.0f, 0.0f); // <-- This texture is blank? It's the texture that was just rendered to above.
_spriteBatch.end();
So like I said, the end output is blank. I'm sure the FBO I create is valid since I check with glCheckFramebufferStatus.
It's just when I take the color texture(s) from that FBO and draw them to the screen, they're blank. I don't know where I'm going wrong.
Appreciate any input.
Last Ive checked the gl30 wasnt really ready for prime time in LibGDX.
That said, Ive achieved exactly what you want, but in gl20. Even if you have other reasons to use gl30, perhaps you will find my implementation useful source, video.
Don't forget to rewind your intbuffer:
IntBuffer intBuffer = BufferUtils.newIntBuffer(2);
intBuffer.put(GL30.GL_COLOR_ATTACHMENT0);
intBuffer.put(GL30.GL_COLOR_ATTACHMENT1);
intBuffer.rewind();
I recently started learning opengl and still don't know very much about it. I was following a tutorial and wrote these two shaders:
Vertex Shader:
#version 400
in vec4 s_vPosition;
in vec4 s_vColor;
out vec4 color;
void main() {
color = s_vColor;
gl_Position = s_vPosition;
}
Fragment Shader:
#version 400
in vec4 color;
out vec4 fColor;
void main() {
fColor = color;
}
They compile and work just fine on the desktop with OpenGL 3, but don't compile with OpenGL ES 2 on Android. I tried checking the shader output log, but it returned a blank string. Again, I am very new to this and my mistake is probably very simple, but any help would be highly appreciated.
In OpenGLES2 you don't use the in and out variable prefixes like in 3.0. Instead you use the following keywords:
attribute Values that are passed in to the vertex shader per vertex
varying Values that are passed from the vertex shader to the fragment shader
uniform Global variables that you can set on a shader for all vertices and fragments (includes things like textures, but can also be scalar or vector types).
An attribute corresponds to an in in a vertex shader.
A varying corresponds to an out in a vertex shader and an in in a fragment shader. So, change your vertex shader to this:
attribute vec4 s_vPosition;
attribute vec4 s_vColor;
varying vec4 color;
void main() {
color = s_vColor;
gl_Position = s_vPosition;
}
and your fragment shader to this:
varying vec4 color;
void main() {
gl_FragColor = color;
}
gl_FragColor is a specially defined variable like gl_Position used for outputting the color from a fragment shader.
Using distance field fonts like explained in the Libgdx wiki: https://github.com/libgdx/libgdx/wiki/Distance-field-fonts
Gives good results on android. I'm using the example shader provided.
But when I run the same program on iOS the text is filled up with white everywhere (see image below).
Is distance field not supported by iOS or do I need to add/change something else to get it to work?
shader code font.vert:
uniform mat4 u_projTrans;
attribute vec4 a_position;
attribute vec2 a_texCoord0;
attribute vec4 a_color;
varying vec4 v_color;
varying vec2 v_texCoord;
void main() {
gl_Position = u_projTrans * a_position;
v_texCoord = a_texCoord0;
v_color = a_color;
}
font.frag:
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D u_texture;
varying vec4 v_color;
varying vec2 v_texCoord;
const float smoothing = 1.0/16.0;
void main() {
float distance = texture2D(u_texture, v_texCoord).a;
float alpha = smoothstep(0.5 - smoothing, 0.5 + smoothing, distance);
gl_FragColor = vec4(v_color.rgb, alpha);
}
Loading the fonts like this:
BitmapFont bf = new BitmapFont(Gdx.files.internal("fonts/mont-b.fnt"));
bf.getRegion().getTexture().setFilter(TextureFilter.MipMapLinearNearest, TextureFilter.Linear);
and the shader:
fontShader = new ShaderProgram(Gdx.files.internal("shader/font.vert"), Gdx.files.internal("shader/font.frag"));
I think the issue might be that you don't have mip mapping enabled on your font texture, because you simply used the simple BitmapFont constructor with no TextureRegion argument, so it loads the Texture with the assumption of no mip maps.
You must create a Texture that supports mip maps like this, as shown on the libgdx wiki:
Texture texture = new Texture(Gdx.files.internal("yourFont.png"), true); //true to enable mip maps
texture.setFilter(TextureFilter.MipMapLinearNearest, TextureFilter.Linear);
Then you can load your BitmapFont using the mip map Texture, so the BitmapFont won't create its own Texture with no mip maps:
BitmapFont bf = new BitmapFont(Gdx.files.internal("fonts/mont-b.fnt"), new TextureRegion(texture));
Your code is fine. However, iOS uses pre-multiplied alpha for all images by default. So, the image that you are using is converted to pre-multiplied alpha format and not the same that you supplied.
Not sure where exactly, but you need to disable pre-multiplied alpha for the image and the "boxes" will be gone.
And no, your problem is not related to mipmapping.