1
votes

I am making a game engine, and in there I have a class which loads OBJ models. The class itself works perfectly, however, the issue I am getting is that when I render any model with textures I will always get the error (1282) Invalid Operation. I have tried different things in the code, and i have found out that it is specifically the texture() call in the fragment shader that causes this issue. I have a custom class to move textures into texture units based on which units are open, here is that class:

public class GLTextureHandler{
    private static ConcurrentHashMap<Integer,Integer> texRef=new ConcurrentHashMap<Integer,Integer>();
    public static final int texUnits=GL11.glGetInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
    private static Integer[] inUse=new Integer[texUnits];
    static{
        for(int i=0;i<inUse.length;i++){
            inUse[i]=0;
        }
        inUse[0]=1;
    }
    public static void registerTex(int tex){
        texRef.put(tex,-1);
    }
    public static int useTex(int tex){
        if(!texRef.containsKey(tex))
            registerTex(tex);
        int slot=texRef.get(tex);
        if(slot!=-1)
            return slot;
        int cnt=0;
        for(int u:inUse){
            System.out.println("Checking CNT ("+cnt+"), u is "+u);
            if(u==0){
                glActiveTexture(GL_TEXTURE0+cnt);
                glBindTexture(GL_TEXTURE_2D,tex);
                inUse[u]=1;
                texRef.put(tex,cnt);
                System.out.println("putting in slot "+cnt);
                return cnt;
            }
            cnt++;
        }
        glActiveTexture(GL_TEXTURE0+texUnits-1);
        glBindTexture(GL_TEXTURE_2D,tex);
        inUse[texUnits-1]=1;
        texRef.put(tex,texUnits-1);
        return texUnits-1;
    }
    public static void openSlot(int tex){
        if(!texRef.containsKey(tex))
            return;
        int slot=texRef.get(tex);
        if(slot!=-1)
            inUse[slot]=0;
    }
    public static boolean hasTex(int tex){
        return texRef.containsKey(tex);
    }
}

The class puts textures into a slot when useTex() is called, and returns which slot it was put in. I call this inside my DetailedVAO class, which simply renders the VAO after updating the uniforms for the materials (modelview matrix is handled inside the model class). It also tells the shader which texture unit the texture is in, and as far as I know it rcarrectly binds the texture. The The detailedVAO class is this:

class DetailedVAO{
    private Material mtl;
    private int vao,ksloc,kaloc,kdloc,texLoc,shinyLoc;
    private int texRef;

    public DetailedVAO(int vao,Material mtl,int ksloc,int kaloc,int kdloc,int texLoc,int shinyLoc){
        this.vao=vao;
        this.mtl=mtl;
        this.kaloc=kaloc;
        this.kdloc=kdloc;
        this.ksloc=ksloc;
        this.texLoc=texLoc;this.shinyLoc=shinyLoc;
        texRef=(mtl.tex()==null?-1:mtl.tex().getTextureID());
        GLTextureHandler.registerTex(texRef);
    }
    public void render(){
        Vec3 Ks=(mtl.getKs()==null?new Vec3(1):mtl.getKs());
        Vec3 Ka=(mtl.getKa()==null?new Vec3(.5f):mtl.getKa());
        Vec3 Kd=(mtl.getKd()==null?new Vec3(1):mtl.getKd());

        GL20.glUniform3f(ksloc,Ks.x,Ks.y,Ks.z);
        GL20.glUniform3f(kaloc,Ka.x,Ka.y,Ka.z);
        GL20.glUniform3f(kdloc,Kd.x,Kd.y,Kd.z);
        GL20.glUniform1f(shinyLoc,mtl.getShiny());

        int aSlot=GLTextureHandler.useTex(texRef);
        GL20.glUniform1f(texLoc,aSlot);

        glBindVertexArray(vao);
        glDrawArrays(GL_TRIANGLES, 0, Model.fvaoSize/4);
    }
}

The Vertex Shader:

#version 330

in vec4 position;
in vec3 normals;
in vec2 texCoords;

uniform mat4 view;
uniform mat4 projection;
uniform mat4 model;
uniform mat4 normal;

uniform vec3 u_lightPosition;
uniform vec3 u_cameraPosition;

uniform vec3 Ks;
uniform vec3 Ka;
uniform vec3 Kd;

out vec3 o_normal;
out vec3 o_toLight;
out vec3 o_toCamera;
out vec2 o_texcoords;

void main()
{
    vec4 worldPosition=model*position;

    o_normal = normalize(mat3(normal) * normals);

   // direction to light
   o_toLight = normalize(u_lightPosition - worldPosition.xyz);

   // direction to camera
   o_toCamera = normalize(u_cameraPosition - worldPosition.xyz);

   // texture coordinates to fragment shader
   o_texcoords = texCoords;

    gl_Position=projection*view*worldPosition;
}

The Fragment Shader works if i just use the Blinn-Phong values:

#version 330
out vec4 outputColor;

uniform vec4 color;

uniform mat4 view;
uniform mat4 projection;
uniform mat4 model;

uniform vec3 u_lightAmbientIntensitys; // = vec3(0.6, 0.3, 0);
uniform vec3 u_lightDiffuseIntensitys; // = vec3(1, 0.5, 0);
uniform vec3 u_lightSpecularIntensitys; // = vec3(0, 1, 0);

// parameters of the material and possible values
uniform vec3 u_matAmbientReflectances; // = vec3(1, 1, 1);
uniform vec3 u_matDiffuseReflectances; // = vec3(1, 1, 1);
uniform vec3 u_matSpecularReflectances; // = vec3(1, 1, 1);
uniform float u_matShininess;

uniform sampler2D u_diffuseTexture;

uniform vec3 Ks;
uniform vec3 Ka;
uniform vec3 Kd;

in vec3 o_normal;
in vec3 o_toLight;
in vec3 o_toCamera;
in vec2 o_texcoords;

vec3 ambientLighting()
{
   return Ka * u_lightAmbientIntensitys;
}

// returns intensity of diffuse reflection
vec3 diffuseLighting(in vec3 N, in vec3 L)
{
   // calculation as for Lambertian reflection
   float diffuseTerm = clamp(dot(N, L), 0, 1) ;
   return Kd * u_lightDiffuseIntensitys * diffuseTerm;
}

// returns intensity of specular reflection
vec3 specularLighting(in vec3 N, in vec3 L, in vec3 V)
{
   float specularTerm = 0;

   // calculate specular reflection only if
   // the surface is oriented to the light source
   if(dot(N, L) > 0)
   {
      // half vector
      vec3 H = normalize(L + V);
      specularTerm = pow(dot(N, H), u_matShininess);
   }
   return Ks * u_lightSpecularIntensitys * specularTerm;
}

void main(void)
{
   // normalize vectors after interpolation
   vec3 L = normalize(o_toLight);
  vec3 V = normalize(o_toCamera);
  vec3 N = normalize(o_normal);

   // get Blinn-Phong reflectance components
   vec3 Iamb = ambientLighting();
   vec3 Idif = diffuseLighting(N, L);
   vec3 Ispe = specularLighting(N, L, V);

   // diffuse color of the object from texture
   vec3 diffuseColor = vec3(texture(u_diffuseTexture, o_texcoords.xy));
   // combination of all components and diffuse color of the object
   outputColor.xyz = diffuseColor*(Iamb+Ispe+Idif);
   outputColor.a = 1;
}
1
Where exactly do you get the INVALID_OPERATION - have you moved around your glGetError? It's weird. The only Idea I would get about the fragment shader would be to replace the texture lookup like this: vec3 diffuseColor = texture(u_diffuseTexture, o_texcoords.xy).rgb;St0fF
Yes, please check glGetError after every opengl call (put it in #ifdef _DEBUG brackets if you want).Robinson
I have the debug option enabled, so it is called after every call. Like I said in the post, removing the texture() fixes the issue. @St0fF, I have tried that too, with the same error. Thanks for looking though!Nick Johnson
Ok, I dunno about this combination. To me normal OpenGL is C/C++. There you have to place glGetError calls. Especially I am confused that you can mangle the error that far into the fragment shader. So for now I'll just assume glDrawArrays threw the error. It doesn't appear without texturing - have you correctly set all necessary texture parameters? GL_MIN_FILTER, GL_MAG_FILTER, GL_TEXTURE_WRAP_S, and GL_TEXTURE_WRAP_T?St0fF
If you use some kind of debug option, it should really tell you which OpenGL call triggered the error. Otherwise it would be fairly useless, and you would be better off placing glGetError() calls yourself. It's really easy to narrow down where the error comes from. And once you do, it tends to be very obvious what's causing it.Reto Koradi

1 Answers

1
votes
  • This may be the source of your problems:

    GL20.glUniform1f(texLoc,aSlot);
    

    It should be

    GL20.glUniform1i(texLoc,aSlot); // i not f
    
  • EDIT: I did not read the code closely enough regarding setting glActiveTexture. The comment I made was false.

  • You're not unbinding your VAO:

    glBindVertexArray(vao);
    glDrawArrays(GL_TRIANGLES, 0, Model.fvaoSize/4);
    glBindVertexArray( 0 ); // <- Probably want this