I have a project I'm working on in libGDX. I'm running tests on a distance field font and I've run into issues while compiling the shader on my Android phone (Galaxy Core 2 4.4.2). When deployed on my phone I get errors while the desktop app works fine (mostly - I'll get to that).
I'll take you through what I've been trying.
I want to be able to enable and disable having a font border during run time, and I can do this fine on the desktop app using the following shader and methods.
.frag:
#ifdef GL_ES
precision mediump float;
#else
#define LOWP
#endif
uniform sampler2D u_texture;
uniform float u_lower;
uniform float u_upper;
varying vec4 v_color;
uniform vec4 u_outlineColor;
uniform float u_enableOutline;
varying vec2 v_texCoord;
const float smoothing = 1.0/12.0;
const float outlineWidth = 3.0/12.0; //will need to be tweaked
const float outerEdgeCenter = 0.5 - outlineWidth; //for optimizing below calculation
void main() {
float distance = texture2D(u_texture, v_texCoord).a;
if (u_enableOutline > 0){
float alpha = smoothstep(outerEdgeCenter - smoothing, outerEdgeCenter + smoothing, distance);//Bigger to accomodate outline
float border = smoothstep(0.45 - smoothing, 0.55 + smoothing, distance);
gl_FragColor = vec4( mix(u_outlineColor.rgb, v_color.rgb, border), alpha );
}
else{
float alpha = smoothstep(0.5 - smoothing, 0.5 + smoothing, distance);
gl_FragColor = vec4(v_color.rgb, alpha);
}
}
.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;
}
With the distance font method to enable / disable the outline being:
public void enableOutline(float enable) {
ENABLE_OUTLINE = enable;
}
Where ENABLE_OUTLINE is passed to the shader by
distanceFieldShader.setUniformf("u_enableOutline", ENABLE_OUTLINE);
In this set up, running on my phone gives the following error:
"cannot compare float to int"
referencing this line in the .frag
if (u_enableOutline > 0){
Fair enough I say, so I change the data type like so:
uniform int u_enableOutline;
And the method to pass through an int:
public void enableOutline(int enable) {
ENABLE_OUTLINE = enable;
}
BUT there is no way to pass an int to the shader (which is why I chose to use floats, see this image: http://imgur.com/nVTN12i) and because of this my method to enable the outline doesn't work due to mixing up data types.
So my question is: can I get around this somehow so that I can enable and disable a border on my phone given these constraints?
It sounds like the API you are using does not offer you the possibility to use bool and int as Uniforms. The solution of using floats to circumvent this seems to be a good idea.
In your example, the problem you are having is because, unlike C and java, the GLSL compiler does not implicitly convert ints to floats.
what you need to do, is tell the compiler the type of your "0".
By using the syntax, 0.0, the compiler knows that your constant is a float and not an integer.
if (u_enableOutline > 0.0){
should fix your problem
Related
I'm devloping a android OpenGL ES app, here is my fragment shader snippet:
uniform sampler2D inputImageTexture;
varying highp vec2 textureCoordinate;
uniform lowp vec2 vignetteCenter;
uniform lowp vec3 vignetteColor;
uniform highp float vignetteStart;
uniform highp float vignetteEnd;
void main()
{
lowp vec4 sourceImageColor = texture2D(inputImageTexture, textureCoordinate);
lowp float d = distance(textureCoordinate, vec2(vignetteCenter.x, vignetteCenter.y));
lowp float percent = smoothstep(vignetteStart, vignetteEnd, d);
gl_FragColor = vec4(mix(sourceImageColor.rgb, vignetteColor, percent), sourceImageColor.a);
}
My problem is that I want to bind a variable with GLSL's vignetteCenter and vignetteColor above, I don't know whick kind of Java Type object correspond to vec2 and vec3? GLES20.glUniform2f or GLES20.glUniform2fv, which one should I use?
GLES20.glUniform2f will allow you to assign ONE vector of 2 floats to your uniform variable.
GLES20.glUniform2fv will allow you to assign N vectors of 2 floats to your uniform variable.
So, given two floats f1 and f2, you can either use
GLES20.glUniform2f(myVariablePosition,f1,f2);
or
float[] myVector = {f1,f2};
GLES20.glUniform2f(myVariablePosition,1,myVector);
where 1 there means only 1 vector of 2 floats is passed.
If you wanted to pass an array of vectors of 2 floats then you would do:
float[] myVector = {f1,f2,....,f(N*2)};
GLES20.glUniform2f(myVariablePosition,N,myVector);
This is my fragment shader code. Its an implementation of Gradient Map function.
varying vec2 v_TextureCoord;
uniform samplerExternalOES sTexture;
uniform sampler2D u_GradientTexture;
uniform float u_FilParOne;
const highp vec3 W = vec3(0.2989, 0.5870, 0.1140);
void main() {
lowp vec4 textureColor = texture2D(sTexture, v_TextureCoord);
float gray = dot(textureColor.rgb, W);
float samplePoint = (0.2 + ((1.0 - 0.2) * gray));
vec4 map = texture2D(u_GradientTexture, vec2(samplePoint, 5.0));
gl_FragColor = vec4(vec3(map), textureColor.a);
}
Note the line
float samplePoint = (0.2 + ((1.0 - 0.2) * gray));
the 0.2 there is something I would like to replace with a parameter. so I used the uniform u_FilParOne. But when I replace the 0.2 with that variable like this:
float samplePoint = (u_FilParOne + ((1.0 - u_FilParOne) * gray));
my program runs but the Screen just keeps cycling through random colors.
when I replace all the code in the main function with
gl_FragColor = vec4(u_FilParOne, u_FilParOne, u_FilParOne, 1.0);
My screen turns gray and when i change the value of the uniform from my java code with the lines :
filterParameterOne=0.2f;
glUniform1f(mUniformFilterParameterOneHandle, filterParameterOne);
I can control the lightness and darkness of the gray, Which means that the value is being sent correctly from Java to OpenGL.
Is there something I am missing here? Why can't I use the uniform in the original program itself?
edit--
I've updated my code after TenFour04s answer but still just shows black.
I've updated my libgdx and its required me to use gl20 which has lead me to make a few changes
most of it works fine except when trying to do texture the mesh. This currently shows surface mesh as black and doesnt show the ground mesh at all. with some changes I can get it to show both surface and ground meshes as black.
I've played around with binding and the order of surTexture.bind and grdTexture.bind and using numbers less than 16 and render and i've got it to use the surface texture as the texture for everything except the surface and ground.
Can anyone see where I might be going wrong with this?
// creating a mesh with maxVertices set to vertices,size*3
groundMesh = new Mesh(true, vertices.size*3, vertices.size*3,
new VertexAttribute(Usage.Position,2,ShaderProgram.POSITION_ATTRIBUTE),
new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE));
groundMesh.setVertices(temp);
short[] indices = new short[vertices.size*2];
for(int i=0;i<vertices.size*2;i++){
indices[i] = (short)i;
}
groundMesh.setIndices(indices);
surfaceMesh = new Mesh(true, vertices.size*3, vertices.size*3,
new VertexAttribute(Usage.Position,3,ShaderProgram.POSITION_ATTRIBUTE),
new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE));
...
grdTexture = new Texture(Gdx.files.internal("data/img/leveltest/ground.png"));
// Gdx.graphics.getGL20().glActiveTexture(GL20.GL_TEXTURE16);
//says that setWrap and SetFilter bind the texture so I thought I might have to set
//activetexture here but does nothing.
grdTexture.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
grdTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
surTexture = new Texture(Gdx.files.internal("data/img/leveltest/surface.png"));
// Gdx.graphics.getGL20().glActiveTexture(GL20.GL_TEXTURE17);
surTexture.setWrap(TextureWrap.Repeat, TextureWrap.ClampToEdge);
//TODO change these filters for better quality
surTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
drawWorld gets called inside render()
public void drawWorld(SpriteBatch batch,OrthographicCamera camera) {
batch.begin();
batch.setProjectionMatrix(camera.combined);
layers.drawLayers(batch);
if ((spatials != null) && (spatials.size > 0)){
for (int i = 0; i < spatials.size; i++){
spatials.get(i).render(batch);
}
}
batch.end();
drawGround(camera);
}
private void drawGround(OrthographicCamera camera){
shader.begin();
shader.setUniformMatrix("u_projTrans", camera.combined);
grdTexture.bind(0);
shader.setUniformi("u_texture", 0);
//changed GL_TRIANGLES to GL_TRIANGLE_STRIP to render meshes correctly after changing to GL20
groundMesh.render(shader, GL20.GL_TRIANGLE_STRIP);
surTexture.bind(0);
shader.setUniformi("u_texture", 0);
surfaceMesh.render(shader, GL20.GL_TRIANGLE_STRIP);
shader.end();
}
fragment.glsl
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
varying LOWP vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
void main()
{
gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
}
vertex.glsl
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord;
uniform mat4 u_projTrans;
varying vec4 v_color;
varying vec2 v_texCoords;
void main() {
v_color = a_color;
v_color.a = v_color.a * (256.0/255.0);
v_texCoords = a_texCoord;
gl_Position = u_projTrans * a_position;
}
In your vertex shader, you are using a_texCoord, but in your mesh constructor, you have effectively named your attributes a_texCoord16 and a_texCoord17 by using ShaderProgram.TEXCOORD_ATTRIBUTE+"16" and ShaderProgram.TEXCOORD_ATTRIBUTE+"17".
Since you are not multi-texturing, I would just replace those with "a_texCoord".
It looks like maybe you are conflating attribute name suffixes with what texture units are, although the two concepts are not necessarily related. The reason you might want to add number suffixes to your texCoords is if your mesh has multiple UV's for each vertex because it is multi-tetxtured. But really you can use any naming scheme you like. The reason you might want to bind to a unit other than 0 is if you're multi-texturing on a single mesh so you need multiple textures bound at once. So if you actually were multi-texturing, using attribute suffixes that match texture unit numbers might help avoid confusion when you are trying to match UV's to textures in the fragment shader.
ok so it turns out the problem was the vertex shader
They code from here doesnt work.
here is the working shader
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord;
uniform mat4 u_projTrans;
varying vec4 v_color;
varying vec2 v_texCoords;
void main() {
v_color = vec4(1, 1, 1, 1);
v_texCoords = a_texCoord;
gl_Position = u_projTrans * a_position;
}
I'm very new to OpenGL and LibGdx. I started with these tutorials but wanted to apply a phong texture. I've tried to merge a number of examples but am having issues.
I've got a sphere and spinning cube on the center of the screen. I've still got hundreds of things to work out but for the moment, I don't understand why LibGdx is reporting that my uniform material can't be found...
Exception in thread "LWJGL Application" com.badlogic.gdx.utils.GdxRuntimeException: java.lang.IllegalArgumentException: no uniform with name 'uMvpMatrix' in shader
Pixel Shader
I don't believe the Fragment shader is relevant but it's at the bottom in case.
#version 120
uniform mat4 uMvpMatrix;
varying vec3 diffuseColor;
// the diffuse Phong lighting computed in the vertex shader
varying vec3 specularColor;
// the specular Phong lighting computed in the vertex shader
varying vec4 texCoords; // the texture coordinates
void main()
{
vec3 normalDirection =
normalize(gl_NormalMatrix * gl_Normal);
vec3 viewDirection =
-normalize(vec3(gl_ModelViewMatrix * gl_Vertex));
vec3 lightDirection;
float attenuation;
if (0.0 == gl_LightSource[0].position.w)
// directional light?
{
attenuation = 1.0; // no attenuation
lightDirection =
normalize(vec3(gl_LightSource[0].position));
}
else // point light or spotlight (or other kind of light)
{
vec3 vertexToLightSource =
vec3(gl_LightSource[0].position
- gl_ModelViewMatrix * gl_Vertex);
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance; // linear attenuation
lightDirection = normalize(vertexToLightSource);
if (gl_LightSource[0].spotCutoff <= 90.0) // spotlight?
{
float clampedCosine = max(0.0, dot(-lightDirection,
gl_LightSource[0].spotDirection));
if (clampedCosine < gl_LightSource[0].spotCosCutoff)
// outside of spotlight cone?
{
attenuation = 0.0;
}
else
{
attenuation = attenuation * pow(clampedCosine,
gl_LightSource[0].spotExponent);
}
}
}
vec3 ambientLighting = vec3(gl_LightModel.ambient);
// without material color!
vec3 diffuseReflection = attenuation
* vec3(gl_LightSource[0].diffuse)
* max(0.0, dot(normalDirection, lightDirection));
// without material color!
vec3 specularReflection;
if (dot(normalDirection, lightDirection) < 0.0)
// light source on the wrong side?
{
specularReflection = vec3(0.0, 0.0, 0.0);
// no specular reflection
}
else // light source on the right side
{
specularReflection = attenuation
* vec3(gl_LightSource[0].specular)
* vec3(gl_FrontMaterial.specular)
* pow(max(0.0, dot(reflect(-lightDirection,
normalDirection), viewDirection)),
gl_FrontMaterial.shininess);
}
diffuseColor = ambientLighting + diffuseReflection;
specularColor = specularReflection;
texCoords = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
Setup
shader = new ShaderProgram(vertexShader, fragmentShader);
mesh = Shapes.genCube();
mesh.getVertexAttribute(Usage.Position).alias = "a_position";
Render
...
float aspect = Gdx.graphics.getWidth() / (float) Gdx.graphics.getHeight();
projection.setToProjection(1.0f, 20.0f, 60.0f, aspect);
view.idt().trn(0, 0, -2.0f);
model.setToRotation(axis, angle);
combined.set(projection).mul(view).mul(model);
Gdx.gl20.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
shader.begin();
shader.setUniformMatrix("uMvpMatrix", combined);
mesh.render(shader, GL20.GL_TRIANGLES);
shader.end();
Stack Trace
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:113)
Caused by: java.lang.IllegalArgumentException: no uniform with name 'uMvpMatrix' in shader
at com.badlogic.gdx.graphics.glutils.ShaderProgram.fetchUniformLocation(ShaderProgram.java:283)
at com.badlogic.gdx.graphics.glutils.ShaderProgram.setUniformMatrix(ShaderProgram.java:539)
at com.badlogic.gdx.graphics.glutils.ShaderProgram.setUniformMatrix(ShaderProgram.java:527)
at com.overshare.document.Views.Test.onRender(Test.java:150)
...
Fragment Shader
#ifdef GL_ES
precision mediump float;
#endif
precision mediump float;
varying vec4 v_Color;
void main()
{
gl_FragColor = v_Color;
}
Can someone please tell me what I'm missing?
I ran into something similar. I think because your shader doesn't use the "uMvpMatrix" uniform, it declaration gets optimized out, and so its "not there" when you go to set it. If you change your shader to reference the matrix in some way, you should get farther.
See (indirectly)
Do (Unused) GLSL uniforms/in/out Contribute to Register Pressure?
I believe there are ways of developing and compiling shaders offline, so for a complex shader it may make sense to develop it outside of Libgdx (hopefully you'd get better error messages). Libgdx is just passing the giant string on to the lower layers, it doesn't do much with the shader itself, so there shouldn't be compatibility issues.
Also, problem could be in precision specifier. On device(Nexus S) the next uniform defining will throw the same error:
uniform float yShift;
Using precision specifier solves the problem:
uniform lowp float yShift;
LibGDX allows to check shader compilation and get error log:
if(!shader.isCompiled()){
String log = shader.getLog();
}
Finally, there's flag to ignore shader errors:
ShaderProgram.pedantic = false;
At the beginning of my project, I used simple Strings for filling my both Shaders with code. This looked like this:
public final static String chunkDefaultVertexInit = ""
+constantParameters
+"precision mediump float;"
+"uniform mat4 mPMatrix;"
+"uniform mat4 mVMatrix;"
+"uniform mat4 mMMatrix;"
+"uniform mat4 mMVMatrix;"
+"attribute vec4 Vertex;"
+"attribute vec3 Normal;"
+"attribute vec2 TexCoord;"
+"varying vec3 normal;"
+"varying vec2 uv;"
+"varying vec4 positionM;"
+"varying vec4 positionMV;";
etc....
This worked for me, but it was not really clearly. So I thought about how I could make my code a little bit more clean and clearly for everybody. My idea was, to put my whole bunch of code in a real .cc - file and move it into the res/raw folder. No sooner said than done.
I wanted to read out my code via Inputstreams and save it into a String. That also worked fine, and so I fed the shader the String source.
So... now there happend to be a problem, and as I said, I didn't get it yet. I even made me a little bit angry about myself, because I thought about an easy way of fix it and I don't see it.
I even did show my source code I put in... but it looks correct! o.O
Log.i("Llama3D Shader",shaderCode);
(Don't worry about the weird "Debug ID," it's the projects name)
Here's the source code for the shaders:
Vertexshader:
//vertexshader
precision mediump float;
uniform mat4 mPMatrix;
uniform mat4 mVMatrix;
uniform mat4 mMMatrix;
uniform mat4 mMVMatrix;
attribute vec4 aVertex;
attribute vec3 aNormal;
attribute vec2 aTexCoord;
varying vec2 vecTexCoord;
varying vec3 vecNormal;
varying vec4 vecVertex[2];
void main() {
gl_Position = mPMatrix * mMVMatrix * aVertex;
vecVertex[0] = mMMatrix * aVertex;
vecVertex[1] = mMVMatrix * aVertex;
vecTexCoord = aTexCoord;
vecNormal = normalize(vec3(mMMatrix * -vec4(aNormal,0.0)));
}
Fragmentshader:
#define MAX_POINT_LIGHTS 4
precision mediump float;
varying vec2 vecTexCoord;
varying vec3 vecNormal;
varying vec4 vecVertex[2];
uniform vec3 uVecEye;
uniform vec3 uPointLightPosition[MAX_POINT_LIGHTS];
uniform vec3 uPointLightColor[MAX_POINT_LIGHTS];
uniform sampler2D textureHandle;
vec3 V = normalize(uVecEye.xyz-vecVertex[1].xyz);
vec3 N = vNormal;
vec3 vecLight[MAX_POINT_LIGHTS];
vec4 pointDiffuse = vec4(0.0);
vec4 pointSpecular = vec4(0.0);
vec4 ambient = vec4(0.2,0.2,0.2,1.0);
vec4 color = vec4(1.0,1.0,1.0,1.0);
vec4 matSpec = vec4(1.0,1.0,1.0,1.0);
vec4 lightSpec = vec4(1.0,1.0,1.0,1.0);
vec4 spec = matSpec * lightSpec;
float shininess = 20.0;
void main() {
for (int i=0;i<MAX_POINT_LIGHTS;i++) {
vecLight[i].xyz = vecVertex[0].xyz - uPointLightPosition[i].xyz;
float vecDistance = length(vecLight[i].xyz);
if (vecDistance<=25.0) {
vecDistance = 1.0 - max(0.0,vecDistance)/25.0;
vec3 L = normalize(vecLight[i]);
vec3 R = normalize(reflect(L,N));
float LND = max(0.0,dot(N,L)) * vecDistance;
pointDiffuse += color * vec4(uPointLightColor[i].xyz,0.0) * LND;
if (shininess!=0.0 && spec!=0.0) {
pointSpecular += spec * pow(max(0.0,dot(R,V)),shininess) * LND;
} else {
pointSpecular += vec4(0.0,0.0,0.0,0.0);
}
}
}
vec4 colorTexture = texture2D(textureHandle,vec2(+vTexCoord.x,-vTexCoord.y));
gl_FragColor = ambient + colorTexture * pointDiffuse + pointSpecular;
}
Every time I try to run the program, the ShaderlogInfo and ProgramlogInfo say to me:
Invalid fragment shader. Link cannot proceed.*
Am I crazy or just blind?!
I hope you know an answer... I really don't know any... please help me!
The log you got is from the Program linking stage, glGetProgramInfoLog.
What you need to debug is the Fragment Shader log, glGetShaderInfoLog.
Something along these lines:
def _compile(self, source):
ptr = cast(c_char_p(source), POINTER(c_char))
glShaderSource(self.id, 1, byref(ptr), None)
glCompileShader(self.id)
status = c_int(0)
glGetShaderiv(self.id, GL_COMPILE_STATUS, byref(status))
log = self.check()
print(log),
if not status.value:
raise Exception(log)
def check(self):
length = c_int(0)
glGetShaderiv(self.id, GL_INFO_LOG_LENGTH, byref(length))
log = create_string_buffer(length.value)
glGetShaderInfoLog(self.id, length.value, None, log)
return log.value
Though this is not in java but in python, it should give you an idea of how to get your shader compile log.
Compiling your shaders in my environment gives me this log which may or may not be useful to you:
Vertex shader was successfully compiled to run on hardware.
WARNING: 0:2: warning(#260) Keyword 'precision' is supported in GLSL 1.3
Fragment shader failed to compile with the following errors:
WARNING: 0:2: warning(#260) Keyword 'precision' is supported in GLSL 1.3
ERROR: 0:14: error(#143) Undeclared identifier vNormal
WARNING: 0:14: warning(#402) Implicit truncation of vector from size 1 to size 3.
ERROR: 0:50: error(#143) Undeclared identifier vTexCoord
ERROR: 0:50: error(#216) Vector field selection out of range 'y'
ERROR: error(#273) 4 compilation errors. No code generated