1

いくつかの 2D イメージを、ユーザーが回転できる 3D ボリュームに変換するボリューム レンダリング プログラムを作成しました。ポイントの周りの各方向のグラデーションを取得して、3D テクスチャ (ライティング用) の各ポイントの法線を計算する必要があります。

法線を計算するには、フラグメント シェーダー内で 6 つの追加のテクスチャ アクセスが必要です。プログラムは、これらの余分なテクスチャ アクセスがなくてもはるかに高速であるため、各方向 (x、y、z) の勾配をバイト単位で事前計算し、それを元のテクスチャの BGA チャネルに格納しようとしています。CPU でテストすると、バイトに正しい値が含まれているように見えますが、シェーダーに到達すると、間違っているように見えます。シェーダーから失敗する理由を判断するのは難しいです。勾配値の一部が負であるためだと思います。しかし、テクスチャ タイプを (GL_UNSIGNED_BYTE ではなく) GL_BYTE として指定すると、それはまだ間違っており、元のテクスチャがどのように見えるべきかが台無しになります。データを色としてレンダリングするだけでは、何が問題なのか正確にわかりません。負の値をテクスチャに入れる正しい方法は何ですか? フラグメントシェーダーで値を読み取るときに、値が負であることをどのように知ることができますか?

次のコードは、バイト配列 (byte[] all) からグラデーションを計算し、それを 3D テクスチャとして読み込まれるバイト バッファー (byteBuffer bb) に変換する操作を実行する方法を示しています。関数 'toLoc(x,y,z,w,h,l)' は単純に (x+w*(y+z*h))*4) を返します。これは、3 次元の添え字を 1 次元のインデックスに変換します。画像はグレースケールなので、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

署名されたデータの大規模な配列をシェーダーに取得するより良い方法はありますか?

4

1 に答える 1

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

これを行うには 2 つの方法があります。シェーダーで実行したい作業の量と、制限したい OpenGL のバージョンに応じて異なります。

より多くのシェーダー作業を必要とするバージョンでは、コードももう少し必要になります。ほら、あなたがしたいのは、シェーダーに符号なしバイトを取り、それらを符号付きバイトとして再解釈させることです。

これが通常行われる方法は、[0, 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] の float にマップされます。まさにあなたが望むもの。

于 2013-03-18T03:18:15.457 に答える