Емулация на 3D текстура в шейдър (свързано със субпиксел)

Работя върху проект Unity3D, който за момент разчита на 3D текстура.

Проблемът е, че Unity позволява само на потребителите на Pro да използват Texture3D. Следователно търся алтернатива на Texture3D, може би едноизмерна текстура (въпреки че не е първоначално налична в Unity), която се интерпретира като 3-измерна в шейдъра (който използва 3D текстурата).

Има ли начин да направите това, като същевременно (за предпочитане) запазите информация за субпикселите?

(Добавени са тагове GLSL и Cg, защото тук е същината на проблема)

Редактиране: Проблемът е разгледан и тук: webgl glsl emulate texture3d Това обаче все още не е завършено и работи правилно.

Редактиране: За момента пренебрегвам правилната информация за субпикселите. Така че всяка помощ за конвертиране на 2D текстура, за да съдържа 3D информация, се оценява!

Редактиране: Оттеглих собствения си отговор, тъй като все още не е достатъчен:

    float2 uvFromUvw( float3 uvw ) {
        float2 uv = float2(uvw.x, uvw.y / _VolumeTextureSize.z);
        uv.y += float(round(uvw.z * (_VolumeTextureSize.z - 1))) / _VolumeTextureSize.z;
        return uv;
    }

С инициализация като Texture2D(volumeWidth, volumeHeight * volumeDepth).

През повечето време работи, но понякога показва грешни пиксели, вероятно поради субпикселна информация, която улавя. Как мога да поправя това? Затягането на входа не работи.


person RobotRock    schedule 05.06.2013    source източник


Отговори (3)


Използвам това за моите 3D облаци, ако това помага:

float   SampleNoiseTexture( float3 _UVW, float _MipLevel )
{
    float2  WrappedUW = fmod( 16.0 * (1000.0 + _UVW.xz), 16.0 );    // UW wrapped in [0,16[

    float   IntW = floor( WrappedUW.y );                // Integer slice number
    float   dw = WrappedUW.y - IntW;                    // Remainder for intepolating between slices

    _UVW.x = (17.0 * IntW + WrappedUW.x + 0.25) * 0.00367647058823529411764705882353;   // divided by 17*16 = 272

    float4  Value = tex2D( _TexNoise3D, float4( _UVW.xy, 0.0, 0.0 ) );

    return lerp( Value.x, Value.y, dw );
}

„3D текстурата“ е опакована като 16 среза с ширина 17 пиксела в текстура 272x16, като 17-тата колона на всеки срез е копие на 1-вата колона (режим на обвиване на адреса)... Разбира се, не е позволено mip-mapping с тази техника.

person Patapom    schedule 20.07.2013
comment
Забравих да уточня, че компонентът X на текстурата съдържа стойността на текущия срез, докато компонентът Y съдържа стойността на следващия срез. По този начин можете да лепнете по посока W, като използвате едно докосване на текстурата. Това обаче ограничава тези 3D текстури само до 2 компонента, но това беше добре, тъй като съхраних само една стойност на монохроматичен шум... - person Patapom; 23.08.2013
comment
Това е доста гениално, трябва да кажа! Благодаря за помощта :) - person RobotRock; 30.08.2013
comment
Може ли да разясниш малко повече за магическите числа? Или имаш някаква документация някъде? - person RobotRock; 31.08.2013

Ето кода, който използвам за създаване на 3D текстура, ако това ви притеснява:

static const    NOISE3D_TEXTURE_POT = 4;
static const    NOISE3D_TEXTURE_SIZE = 1 << NOISE3D_TEXTURE_POT;

// <summary>
// Create the "3D noise" texture
// To simulate 3D textures that are not available in Unity, I create a single long 2D slice of (17*16) x 16
// The width is 17*16 so that all 3D slices are packed into a single line, and I use 17 as a single slice width
//  because I pad the last pixel with the first column of the same slice so bilinear interpolation is correct.
// The texture contains 2 significant values in Red and Green :
//      Red is the noise value in the current W slice
//      Green is the noise value in the next W slice
//  Then, the actual 3D noise value is an interpolation of red and green based on the W remainder
// </summary>
protected NuajTexture2D Build3DNoise()
{
    // Build first noise mip level
    float[,,]   NoiseValues = new float[NOISE3D_TEXTURE_SIZE,NOISE3D_TEXTURE_SIZE,NOISE3D_TEXTURE_SIZE];
    for ( int W=0; W < NOISE3D_TEXTURE_SIZE; W++ )
        for ( int V=0; V < NOISE3D_TEXTURE_SIZE; V++ )
            for ( int U=0; U < NOISE3D_TEXTURE_SIZE; U++ )
                NoiseValues[U,V,W] = (float) SimpleRNG.GetUniform();

    // Build actual texture
    int MipLevel = 0;  // In my original code, I build several textures for several mips...
    int MipSize = NOISE3D_TEXTURE_SIZE >> MipLevel;
    int Width = MipSize*(MipSize+1);    // Pad with an additional column
    Color[] Content = new Color[MipSize*Width];

    // Build content
    for ( int W=0; W < MipSize; W++ )
    {
        int Offset = W * (MipSize+1);   // W Slice offset
        for ( int V=0; V < MipSize; V++ )
        {
            for ( int U=0; U <= MipSize; U++ )
            {
                Content[Offset+Width*V+U].r = NoiseValues[U & (MipSize-1),V,W];
                Content[Offset+Width*V+U].g = NoiseValues[U & (MipSize-1),V,(W+1) & (MipSize-1)];
            }
        }
    }

    // Create texture
    NuajTexture2D   Result = Help.CreateTexture( "Noise3D", Width, MipSize, TextureFormat.ARGB32, false, FilterMode.Bilinear, TextureWrapMode.Repeat );
    Result.SetPixels( Content, 0 );
    Result.Apply( false, true );

    return Result;
}
person Patapom    schedule 02.09.2013

Проследих отговора на Patapoms и стигнах до следното. Въпреки това все още е изключен, както трябва да бъде.

float getAlpha(float3 position)
{
    float2  WrappedUW = fmod( _Volume.xz * (1000.0 + position.xz), _Volume.xz );    // UW wrapped in [0,16[

    float   IntW = floor( WrappedUW.y );                // Integer slice number
    float   dw = WrappedUW.y - IntW;                    // Remainder for intepolating between slices

    position.x = ((_Volume.z + 1.0) * IntW + WrappedUW.x + 0.25) / ((_Volume.z + 1.0) * _Volume.x);   // divided by 17*16 = 272

    float4  Value = tex2Dlod( _VolumeTex, float4( position.xy, 0.0, 0.0 ) );

    return lerp( Value.x, Value.y, dw );
}


public int GetPixelId(int x, int y, int z) {
    return y * (volumeWidth + 1) * volumeDepth + z * (volumeWidth + 1) + x;
}

             // Code to set the pixelbuffer one pixel at a time starting from a clean slate
            pixelBuffer[GetPixelId(x, y, z)].r = color.r;

            if (z > 0)
                pixelBuffer[GetPixelId(x, y, z - 1)].g = color.r;

            if (z == volumeDepth - 1 || z == 0)
                pixelBuffer[GetPixelId(x, y, z)].g = color.r;

            if (x == 0) {
                pixelBuffer[GetPixelId(volumeWidth, y, z)].r = color.r;
                if (z > 0)
                    pixelBuffer[GetPixelId(volumeWidth, y, z - 1)].g = color.r;

                if (z == volumeDepth - 1 || z == 0)
                    pixelBuffer[GetPixelId(volumeWidth, y, z)].g = color.r;
            }
person RobotRock    schedule 31.08.2013