我正在为我的游戏做自己的引擎,并且我在着色器方面遇到了很多问题。最令人讨厌的一个是我无法计算灯光,因为片段着色器中的条件永远不会满足,所以永远不要求不同材质/灯光的颜色。我发现问题的代码是:
// Compute lights
vec4 totalColorLighting = vec4(0.0);
for (int indexComputeLights = 0; indexComputeLights < MAX_LIGHTS; indexComputeLights++)
{
if (indexComputeLights < numLights) {
numLights是传递给片段着色器的制服。如果我在if语句之外写totalColorLight = vec4(1,0);
,我的模型将以白色绘制。如果我把它放在里面,就像我想要的那样,屏幕仍然是黑色......
简而言之,显示白色模型的代码是:
for (int indexComputeLights = 0; indexComputeLights < MAX_LIGHTS; indexComputeLights++)
{
totalColorLight = vec4(1,0); // model is white, so here it´s entering
if (indexComputeLights < numLights) {
// code that never get executed
}
}
代码无效:
for (int indexComputeLights = 0; indexComputeLights < MAX_LIGHTS; indexComputeLights++)
{
if (indexComputeLights < numLights) {
totalColorLight = vec4(1,0); // model is black, so here it´s NOT entering
}
}
获得“workarround”的其他方法是将“numLights”更改为,例如2或1这样的常数。我修改了通过eclipse调试器将统一值传递给着色器的变量,并且1,所以应该输入if语句。
我将数据上传到此统一的方式是:
int[] vecNumLights = new int[1];
vecNumLights[0] = numLights;
GLES20.glUniform1iv(gl_numLights_Uniform_Locator, 1, vecNumLights, 0);
快速浏览一下,有人知道我的错误吗?您需要复制更多代码吗?
顺便说一句,我使用4.4.2 Api 19 SDK
编辑1:
我注意到indexComputeLights从一开始就不为零。如果我写这样的条件:
if (indexComputeLights > 0) totalColorLight = totalColorLight + vec4(0.05);
如果条件是
,那么条件越高,模型就越白 if (indexComputeLights > 6)
颜色结束时更加透明和黑色,只是按照我认为的相反顺序。使用for-unroll发生了什么?
EDIT2:
我认为我的问题与此非常相似:http://www.opengl.org/discussion_boards/showthread.php/171366-problem-with-uniform-int-and-for-loop
不同之处在于我没有注意到for循环是无穷无尽的
EDIT3:
我发现numLights(这是一个统一的int)总是为零。我不明白什么是失败的,因为所有其他制服看起来都很好。我在顶点和片段着色器之间共享统一名称,但在顶点着色器中看起来工作正常。
EDIT4:
我想与您分享完整的顶点和片段代码。
顶点着色器:
#pragma glsl
attribute vec3 position;
attribute vec3 normal;
attribute vec2 texCoord;
// matrices we'll need
uniform mat4 inversedTrasposedModelViewMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
// other uniforms, constant or data we are going to use
const int MAX_LIGHTS = 8;
struct LightSourceParameters {
mediump vec3 ambient;
mediump vec3 lightColor;
mediump vec4 position;
mediump float spotExponent;
mediump float spotCutoff; // (range: [0.0,90.0], 180.0)
mediump vec3 spotDirection;
mediump float constantAttenuation;
mediump float linearAttenuation;
mediump float quadraticAttenuation;
};
uniform LightSourceParameters LightSource[MAX_LIGHTS];
uniform lowp int numLights;
// out parameters to fragment shader: varyings
varying vec3 outNormal;
varying vec2 outTextCoord;
varying vec3 outViewVector;
varying vec3 outLightVector[MAX_LIGHTS];
void main(){
// Calculate view-space position coordinate
vec4 P = modelViewMatrix * vec4(position,1.0);
// Calculate the normal in view-space
outNormal = vec3(inversedTrasposedModelViewMatrix * vec4(normal ,0.0));
// Calculate the view vector in view-space space coordinate
outViewVector = -P.xyz;
// Assign the texture coordinate
outTextCoord = texCoord;
// Calculate clip-space position of each vertex
gl_Position = projectionMatrix * P;
// Calculate light vector for all light source
for (int indexLightVector = 0; indexLightVector < MAX_LIGHTS; indexLightVector++){
if (indexLightVector < numLights) {
/* Si no es ambiental: la unica luz que no lleva asociada vector */
if ((length(LightSource[indexLightVector].ambient) == 0.0) /* no ambiental */
&& (LightSource[indexLightVector].position.w != 0.0)){ /* no directional */
/* La luz es o point o spotLight */
outLightVector[indexLightVector] = vec3(modelViewMatrix*LightSource[indexLightVector].position) - P.xyz;
}
else if (length(LightSource[indexLightVector].ambient) == 0.0) { /* no ambiental */
/* La luz es directional: position es un vector,
* lo transformamos con inversedTransposedModelViewMatrix
* y lo negamos para que vaya desde el punto a la luz y no al revés
*/
outLightVector[indexLightVector] = - vec3(inversedTrasposedModelViewMatrix*LightSource[indexLightVector].position);
}
}
}
}
片段着色器:
#pragma glsl
precision mediump float;
const int MAX_LIGHTS = 8;
struct LightSourceParameters {
vec3 ambient;
vec3 lightColor;
vec4 position;
float spotExponent;
float spotCutoff; // (range: [0.0,90.0], 180.0)
vec3 spotDirection;
float constantAttenuation;
float linearAttenuation;
float quadraticAttenuation;
};
uniform LightSourceParameters LightSource[MAX_LIGHTS];
struct MaterialParameters {
vec4 emission;
vec4 ambient;
vec4 diffuse;
sampler2D diffuseTexture;
int hasDiffuseTexture;
vec4 specular;
sampler2D specularTexture;
int hasSpecularTexture;
float shininess;
};
uniform MaterialParameters Material;
uniform lowp int numLights;
varying vec3 outNormal;
varying vec2 outTextCoord;
varying vec3 outViewVector;
varying vec3 outLightVector[MAX_LIGHTS];
/* Declaramos cabecera de funcion, necesaria para que GLSL no diga que la funcion no existe, al definirse despues de main */
vec4 computeLight(in MaterialParameters material, in LightSourceParameters lightSource, in vec3 normal, in vec2 textCoord, in vec3 lightVector, in vec3 halfVector);
void main(){
// Normalize the incoming vectors
vec3 normal = normalize(outNormal);
vec3 viewVector = normalize(outViewVector);
vec3 lightVector[MAX_LIGHTS];
vec3 halfVector[MAX_LIGHTS];
// normalize lightvector, compute half vectors and lights
vec4 totalColorLighting = vec4(0.0);
int indexComputeLights = 0;
for (indexComputeLights; indexComputeLights < MAX_LIGHTS; indexComputeLights++){
if (indexComputeLights < numLights) {
totalColorLighting = totalColorLighting + vec4(0.05);
if (length(LightSource[indexComputeLights].ambient) == 0.0 ){ /* no es ambiental, que no tienen vector */
lightVector[indexComputeLights] = normalize(outLightVector[indexComputeLights]);
}
if (length(LightSource[indexComputeLights].ambient) == 0.0 ){ /* no es ambiental, que no tienen half vector */
halfVector[indexComputeLights] = normalize(outLightVector[indexComputeLights] + outViewVector);
}
LightSourceParameters light = LightSource[indexComputeLights];
vec3 currentLightVector = lightVector[indexComputeLights];
vec3 currentHalfVector = halfVector[indexComputeLights];
/* Si la luz es ambiental, halfVector y lightVector son
* indefinidos para esa luz, pero da igual porque no son
* utilizados en el algoritmo que calcula las luces
*/
totalColorLighting = totalColorLighting + computeLight(Material, light, normal, outTextCoord, currentLightVector, currentHalfVector);
indexComputeLights = indexComputeLights + 1;
}
}
vec4 emission = Material.emission;
// vec4 emission = vec4(0.5);
// totalColorLighting = vec4(0.0);
// Compute emission material
if (length(emission) != 0.0) { /* El material tiene un termino emisivo, es decir, emite luz. Lo andimos al total de color calculado */
totalColorLighting = totalColorLighting + emission;
}
/* Devolvemos el color de fragmento calculado para almacenarlo en el framebuffer */
gl_FragColor = totalColorLighting;
//gl_FragColor = vec4(1.0);
}
vec4 computeLight(in MaterialParameters material, in LightSourceParameters lightSource,
in vec3 normal, in vec2 textCoord, in vec3 lightVector, in vec3 halfVector){
float attenuation = 1.0; // no attenuation
vec4 totalLightingColor = vec4(0.0); // no color
if (length(lightSource.ambient) > 0.0){ // es luz ambiente
totalLightingColor = vec4(lightSource.ambient, 1.0) * material.ambient;
}
else { // Is not ambiental light
if (lightSource.position.w == 0.0) { // es un vector, por lo tanto es una luz direccional
attenuation = 1.0; // no attenuation
}
else { // Is a point light or a spot light
float distanceToLight = length(lightVector);
attenuation = 1.0 / (lightSource.constantAttenuation +
(lightSource.linearAttenuation * distanceToLight) +
(lightSource.quadraticAttenuation * distanceToLight * distanceToLight));
if (lightSource.spotCutoff <= 90.0){ /* Is a spot light */
vec3 spotDirection = normalize(lightSource.spotDirection);
float clampedCosine = max(0.0, dot(-lightVector, spotDirection));
if (clampedCosine < cos(radians(lightSource.spotCutoff))){ /* outside the spotlight cone */
attenuation = 0.0; /* full attenuation */
}
else { /* inside the spotlight cone */
attenuation = attenuation * pow(clampedCosine, lightSource.spotExponent);
}
}
}
// Calculo de los terminos de color: diffuso y especular
vec4 diffuseMaterialTerm = vec4(0.0, 0.0, 0.0, 1.0); /* El canal difuso será opaco y negro hasta que se sobreescriban sus datos */
if (material.hasDiffuseTexture == 0) { /* El canal difuso no tiene textura */
diffuseMaterialTerm = material.diffuse;
}
else if (material.hasDiffuseTexture == 1){
diffuseMaterialTerm = texture2D(material.diffuseTexture, textCoord);
}
vec4 diffuseReflection = attenuation * vec4(lightSource.lightColor, 1.0) * diffuseMaterialTerm * max(0.0, dot(normal, lightVector));
vec4 specularReflection = vec4(0.0);
if (dot(normal, lightVector) < 0.0 ) { // light source in the wrong side
specularReflection = vec4(0.0);
}
else { // light source in the right side
float NdotHV = max(dot(normal, halfVector), 0.0); /* Normal-dot-halfvector */
vec4 specularMaterialTerm = vec4 (0.0, 0.0, 0.0, 1.0); /* El canal especular será opaco y negro hasta que se sobreescriban sus datos */
if (material.hasSpecularTexture == 0){
specularMaterialTerm = material.specular;
}
else if (material.hasSpecularTexture == 1){
specularMaterialTerm = texture2D(material.specularTexture, textCoord);
}
specularReflection = attenuation * pow(NdotHV, material.shininess) * vec4(lightSource.lightColor, 1.0) * specularMaterialTerm;
}
totalLightingColor = diffuseReflection + specularReflection;
}
return totalLightingColor;
}
这是我用来上传这个制服的代码(Java)(numLights):
GLES20.glUseProgram(gl_Program.GetProgramID()); // GetProgramID() returns 3 in my case
int numLights = iLightList.size(); // the scene I want to render has 1 light, so this value is 1
String numLightsString = "numLights";
int gl_numLights_Uniform_Locator = gl_Program.GetUniformLocation(numLightsString); // is 83 in my program
GLES20.glUniform1i(gl_numLights_Uniform_Locator, numLights);
int error = GLES20.glGetError(); // error is always 0
非常感谢任何帮助
编辑5:
我发现了一些很奇怪的东西。如果我在片段着色器中注释此代码,则使用正确的值加载统一的numLights:
// vec4 emission = Material.emission;
// if (length(emission) != 0.0) { /* El material tiene un termino emisivo, es decir, emite luz. Lo añadimos al total de color calculado */
// totalColorLighting = totalColorLighting + emission;
// }
但如果我这样做,我就失去了发光部分的计算......有什么想法吗?
答案 0 :(得分:1)
应该使用GLES20.glUniform1i
,因为GLES20.glUniform1iv
案例适用于uniform int numLights[1];
。我确信您已经拥有,GLES20.glUniform1i
应该在GLES20.glUseProgram
之后并且在绘制任何内容之前。
83
对于某个位置来说相当高,但并非不合理。我对android规范知之甚少,但它肯定可能耗尽统一的位置和内存。也许尝试简化着色器(使用较少的光线进行测试)或将制服放得更高。
[编辑] 为了继续这个想法,当涉及到循环和函数时,GLSL编译器可能真的不直观。当分支开始变得非常大时。您可能只是遇到一些奇怪的GLSL编译器错误。您可以尝试手动展开循环,在for循环条件中抛出indexComputeLights < MAX_LIGHTS && indexComputeLights < numLights
会导致生成的条件嵌套(至少在我的nvidia卡上使用当前驱动程序)。也许用常量替换昂贵的computeLight
调用仅用于测试。我遇到了生成的代码似乎太大的情况,我得到的结果无效而不是编译错误。
lowp
会影响设置制服的能力吗?
获得所需结果的另一种方法(也可能更快)是为每个灯数编译着色器。这可以通过在源代码顶部注入#define NUM_LIGHTS x
来轻松完成。
答案 1 :(得分:-1)
我面临同样的问题 - 即将 int uniform 传递给顶点着色器会导致不可预测的行为在某些硬件上:它在Android模拟器和我的Kindle Fire第二代平板电脑上工作得很好,但它在Galaxy S III上失败了(撞毁手机!)。 我发现手机崩溃了,因为在顶点着色器中,循环
for(int i=0; i<u_Sinks; i++)
u_Sinks 是 int uniform ,似乎是无限的。但我做将1传递给u_Sinks:
GLES20.glUniform1i( mSinksH , 1);
当然我确实确保为mSinksH分配了有效值&gt; = 0:
mSinksH = GLES20.glGetUniformLocation(mProgramH, "u_Sinks");
Log.d("distorted", "mSinksH="+mSinksH);
并且日志打印出mSinksH = 4,这对我来说是正确的。