如何创建具有负值的3d纹理并从着色器中读取它

时间:2013-03-18 02:25:59

标签: opengl byte textures shader jogl

我编写了一个体积渲染程序,可将一些2D图像转换为可由用户旋转的3d体积。我需要通过在点周围的每个方向上采用渐变来计算3d纹理中每个点的法线(用于照明)。

计算法线需要在片段着色器中进行六次额外的纹理访问。没有这些额外的纹理访问,程序要快得多,所以我试图以字节为单位预先计算每个方向(x,y,z)的渐变,并将其存储在原始纹理的BGA通道中。当我在CPU上测试时,我的字节似乎包含正确的值,但当我到达着色器时,它出现错误。很难说明为什么它会从着色器中失败,我认为这是因为某些渐变值是负数。但是,当我将纹理类型指定为GL_BYTE(而不是GL_UNSIGNED_BYTE)时,它仍然是错误的,这会搞砸原始纹理的外观。仅仅通过将数据渲染为颜色,我无法确切地知道出了什么问题。将负值放入纹理的正确方法是什么?当我在片段着色器中读取它时,我怎么知道值是负的?

以下代码显示了如何运行操作以从字节数组(byte [] all)计算渐变,然后将其转换为以3d纹理读入的字节缓冲区(byteBuffer bb)。函数'toLoc(x,y,z,w,h,l)'只返回(x + w *(y + z * h))* 4) - 它将3d下标转换为1d索引。图像是灰度图像,因此我丢弃gba并仅使用r通道保存原始值。其余通道(gba)存储渐变。

        int pixelDiffxy=5;
    int pixelDiffz=1;

    int count=0;  
    Float r=0f;
    byte t=r.byteValue();

    for(int i=0;i<w;i++){
        for(int j=0;j<h;j++){
            for(int k=0;k<l;k++){
                count+=4;
                if(i<pixelDiffxy || i>=w-pixelDiffxy || j<pixelDiffxy || j>=h-pixelDiffxy || k<pixelDiffz || k>=l-pixelDiffz){
                    //set these all to zero since they are out of bounds
                    all[toLoc(i,j,k,w,h,l)+1]=t;//green=0
                    all[toLoc(i,j,k,w,h,l)+2]=t;//blue=0
                    all[toLoc(i,j,k,w,h,l)+3]=t;//alpha=0
                }
                else{

                    int ri=(int)all[toLoc(i,j,k,w,h,l)+0] & 0xff;

                    //find the values on the sides of this pixel in each direction (use red channel)
                    int xgrad1=(all[toLoc(i-pixelDiffxy,j,k,w,h,l)])& 0xff;
                    int xgrad2=(all[toLoc(i+pixelDiffxy,j,k,w,h,l)])& 0xff;

                    int ygrad1=(all[toLoc(i,j-pixelDiffxy,k,w,h,l)])& 0xff;
                    int ygrad2=(all[toLoc(i,j+pixelDiffxy,k,w,h,l)])& 0xff;

                    int zgrad1=(all[toLoc(i,j,k-pixelDiffz,w,h,l)])& 0xff;
                    int zgrad2=(all[toLoc(i,j,k+pixelDiffz,w,h,l)])& 0xff;


                    //find the difference between the values on each side and divide by the distance between them
                    int xgrad=(xgrad1-xgrad2)/(2*pixelDiffxy);
                    int ygrad=(ygrad1-ygrad2)/(2*pixelDiffxy);
                    int zgrad=(zgrad1-zgrad2)/(2*pixelDiffz);

                    Vec3f grad=new Vec3f(xgrad,ygrad,zgrad);

                    Integer xg=(int) (grad.x);
                    Integer yg=(int) (grad.y);
                    Integer zg=(int) (grad.z);

                    //System.out.println("gs are: "+xg +", "+yg+", "+zg);

                    byte gby= (byte) (xg.byteValue());//green channel
                    byte bby= (byte) (yg.byteValue());//blue channel
                    byte aby= (byte) (zg.byteValue());//alpha channel

                    //System.out.println("gba is: "+(int)gby +", "+(int)bby+", "+(int)aby);
                    all[toLoc(i,j,k,w,h,l)+1]=gby;//green
                    all[toLoc(i,j,k,w,h,l)+2]=bby;//blue
                    all[toLoc(i,j,k,w,h,l)+3]=aby;//alpha
                }
            }
        }
    }

ByteBuffer bb=ByteBuffer.wrap(all);
    final GL gl = drawable.getGL();
    final GL2 gl2 = gl.getGL2();
    final int[] bindLocation = new int[1];
    gl.glGenTextures(1, bindLocation, 0);
    gl2.glBindTexture(GL2.GL_TEXTURE_3D, bindLocation[0]);
    gl2.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);//-byte alignment
    gl2.glTexParameteri(GL2.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP);
    gl2.glTexParameteri(GL2.GL_TEXTURE_3D, GL.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP);
    gl2.glTexParameteri(GL2.GL_TEXTURE_3D, GL2.GL_TEXTURE_WRAP_R, GL2.GL_CLAMP);
    gl2.glTexParameteri(GL2.GL_TEXTURE_3D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
    gl2.glTexParameteri(GL2.GL_TEXTURE_3D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
    gl2.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE);
    gl2.glTexImage3D( GL2.GL_TEXTURE_3D, 0,GL.GL_RGBA,
            w, h, l, 0,
            GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, bb );//GL_UNSIGNED_BYTE

是否有更好的方法可以将大量签名数据导入着色器?

1 个答案:

答案 0 :(得分:4)

gl2.glTexImage3D( GL2.GL_TEXTURE_3D, 0,GL.GL_RGBA,
        w, h, l, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, bb );

嗯,有两种方法可以做到这一点,具体取决于你想在着色器中做多少工作,以及你想要限制的OpenGL版本。

需要更多着色器工作的版本还需要更多代码。看,你想要做的是让你的着色器采用无符号字节,然后将它们重新解释为有符号字节。

通常这样做的方法是传递无符号的标准化字节(正如你所做的那样),它会在[0,1]范围内产生浮点值,然后通过乘以2来扩展该范围。减去1,在[-1,1]范围内产生数字。这意味着您的上传代码需要使用[-128,127]有符号字节,并通过向它们添加128将它们转换为[0,255]无符号字节。

我不知道如何在 Java 中执行此操作,它似乎根本没有无符号字节类型。你不能只传递一个2的补码字节,并期望它在着色器中工作;这不会发生。字节值-128将映射到浮点值1,这没有用。

如果您可以设法如上所述正确转换数据,那么您的着色器访问必须从[0,1]范围解压缩到[-1,1]范围。

如果您可以访问GL 3.x,那么您可以非常轻松地完成此操作,而不需要更改着色器:

gl2.glTexImage3D( GL2.GL_TEXTURE_3D, 0,GL.GL_RGBA8_SNORM,
        w, h, l, 0, GL.GL_RGBA, GL.GL_BYTE, bb );

图像格式中的_SNORM表示它是带符号的标准化格式。因此,[-128,127]范围内的字节将映射到范围[-1,1]上的浮点数。正是你想要的。