강의

멘토링

로드맵

인프런 커뮤니티 질문&답변

Wonjae Oh님의 프로필 이미지
Wonjae Oh

작성한 질문수

유니티 compute shader에서 convolution matrix 구현

작성

·

524

0

안녕하세요. 벌써 3달째 이 작업만 계속 하고 있어요. 유니티 compute shader(hlsl)에서 convolution matrix를 구현해 가장 가까운 게임 오브젝트까지의 거리를 모드 계산후 추후 효과를 입히는데 적용하고 싶은데 무슨 이유인진 몰라도 제 코드가 너무 이상하게 돌아가요ㅠㅠㅠㅠ

[numthreads(12,12,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    if(int(id.x) - 59 < 0 || int(id.x) + 58 >= MaxWidth || int(id.y) - 59 < 0 || int(id.y) + 58 >= MaxHeight) {
        //Overflow prevention.
        //For now, ignore screen edges.
        return;
    }

    float4 srcPixel = Src[id.xy];

    if(any(srcPixel.rgba > float4(0,0,0,0).rgba)) {
        //Center of convolution matrix on opaque pixel
        float4 displayPixel = float4(0,0,0,0);

        uint visibleGameObjectCount = 0;
        for(int i = 0; i < GameObjectListCount; i++) {
            float4 colorAtDepth = ColorTextureArray.Load(uint3(id.xy, i));
            if(any(colorAtDepth.rgba > float4(0,0,0,0).rgba)) {
                displayPixel += colorAtDepth / GameObjectListCount;
                visibleGameObjectCount++;
            }
        }
        displayPixel *= (GameObjectListCount / visibleGameObjectCount);
        Dest[id.xy] = displayPixel;
        return;
    }
    else {
        bool emptyMatrix = true;

        //Variables to store minimum distances to each game object
            //deltaX and deltaY added here may be negative, because it is distance from id.xy

        //I need separate storages for positive and negative delta
            //Because adding a negative index to omegaX and omegaY makes the added number go out of its bounds
        uint omegaX = 0;
        uint omegaY = 0;

        //Or simply store only absolute values in omegaX and omegaY, store a separate variable that represents -,- or +,+ one for x and one for y for each game object, which is similarly indexed using each game object's index
            //Every time omegaX and omegaY is being updated, signUnsignXY will also be updated 1 for sign, 0 for unsign
        uint signUnsignXY = 0;

        //Will be used for avg. distance calculation
        float indexesForDistance = 0;

        //Counter for visible game objects within each convolution matrix
        float visibleGameObjectCountMatrix = 0;

        //Modified logic:
            //Check if a pixel within the convolution matrix is opaque
            //If it is, start iterating through ColorTextureArray at that location(xy)
            //In order to compare with the previous minimum, for *every* pixel that is *opaque* within
                //that xy's *ColorTextureArray*, use its index to access the actual deltaX and deltaY
                //that belongs to that game object, and if the current value is less than the previously
                //update the values using:
                    //omegaX = omegaX - ((omegaX / 100^i) % 100) + (deltaX * 100^i)
                    //omegaY = omegaY - ((omegaY / 100^i) % 100) + (deltaY * 100^i)
                    //When doing the actual comparison, it must be between pythagorean distances
        //Convolution matrix: size = 57x57(i.e. ~1.5 cm)   
            //Caution: Proceed to read in a peaceful environment    
        for(int i = 29; i > -28; i--) {
            for(int j = 29; j > -28; j--) {
                float4 srcPixel = Src[id.xy - int2(j, i)];
                if(any(srcPixel.rgba > float4(0,0,0,0).rgba)) {
                    emptyMatrix = false;
                    // Dest[id.xy] = float4(1,1,1,1);

                    //Current minimum that may or may not overwrite previous minimum
                    //For the first current minimum, either deltaX or deltaY must > 0, since case 0 is already
                        //taken care of above and the fact that it is visible at this location means a minimum of a GO
                    int deltaX = -j;
                    int deltaY = -i;
                    //Positive by default
                    uint signX = 1;
                    uint signY = 1;
                    
                    if(deltaX < 0) {
                        signX = 0;
                    }
                    if(deltaY < 0) {
                        signY = 0;
                    }
                    //As long as a deltaX or deltaY exists, it's abs is guaranteed to be > 0
                    float minimumDistance = 1 / rsqrt(deltaX * deltaX + deltaY * deltaY);

                    for(int c = 0; c < GameObjectListCount; c++) {
                        float4 minimumDistancePixel = ColorTextureArray.Load(uint3(id.xy - int2(j, i), c));
                        //First check whether each pixel in ColorTextureArray at this location is opaque
                        if(any(minimumDistancePixel.rgba > float4(0,0,0,0).rgba)) {
                            //id.x + omegaX(indexed)
                            uint previousGameObjectAccessor = (uint)pow(100, c);
                            
                            uint previousDeltaX = (omegaX / previousGameObjectAccessor) % 100;
                            //id.y + omegaY(indexed)
                            uint previousDeltaY = (omegaY / previousGameObjectAccessor) % 100;

                            //Previous pythagorean distance
                            float previousMinimumDistance = 0;

                            //Problem: while 1 / (1 / x) = x for most numbers, for 0, it becomes division by 0
                            if(previousDeltaX > 0 || previousDeltaY > 0) {
                                previousMinimumDistance = 1 / rsqrt(previousDeltaX * previousDeltaX + previousDeltaY * previousDeltaY);
                            }

                            if(previousMinimumDistance > minimumDistance) {
                                //New minimum for game object at index c, but it has been mapped already and can be indexed with discrete values

                                //Write new minimum in StorageTextureArray:
                                float4 previousStoredPixel = StorageTextureArray.Load(uint3(id.xy - int2(j, i), c));
                                if(all(previousStoredPixel.rgba <= float4(0,0,0,0).rgba)) {
                                    StorageTextureArray[uint3(id.xy - int2(j, i), c)] = minimumDistancePixel;
                                }
                                
                                //Dest[id.xy - int2(j, i)] = float4(0,0,1,1);

                                //Update omegaX and omegaY while replacing previous deltaX and deltaY
                                omegaX = omegaX - ((omegaX / previousGameObjectAccessor) % 100) + abs(deltaX) * previousGameObjectAccessor;
                                omegaY = omegaY - ((omegaY / previousGameObjectAccessor) % 100) + abs(deltaY) * previousGameObjectAccessor;

                                //Update signUnsignXY (rewrite)
                                uint totalSign = signX * 10 + signY;
                                signUnsignXY = signUnsignXY - ((signUnsignXY / previousGameObjectAccessor) % 100) + totalSign * previousGameObjectAccessor;

                                //Once a mapping has been created for every visible game object, further updating indexes for distance not necessary,
                                //because every game object that was visible at least once in the matrix will also be factored in the weighted color
                                //computation that uses minimum distances of all game objects
                            }
                            //Is this valid logic to count unique game objects using this conditional?
                            else if(previousMinimumDistance == 0) {
                                //Very first instance of a unique game object index at xy
                                    //i.e. this if condition will run only once for each game object index
                                
                                //Why can the matrix detect one game object but not 2???
                                    //This means previousMinimumDistance == 0  is true only once within the convolution matrix
                                    //Even though accessing a game object that hasn't been indexed yet must return 0

                                //Write new minimum in StorageTextureArray:
                                float4 previousStoredPixel = StorageTextureArray.Load(uint3(id.xy - int2(j, i), c));
                                if(all(previousStoredPixel.rgba <= float4(0,0,0,0).rgba)) {
                                    StorageTextureArray[uint3(id.xy - int2(j, i), c)] = minimumDistancePixel;
                                }

                                //Dest[id.xy - int2(j, i)] = float4(0,1/c,0,1);

                                //Update omegaX and omegaY without replacing with deltaX and deltaY (absolute value)
                                omegaX += abs(deltaX) * previousGameObjectAccessor;
                                omegaY += abs(deltaY) * previousGameObjectAccessor;

                                //Update signUnsignX
                                uint totalSign = signX * 10 + signY;
                                signUnsignXY += totalSign * previousGameObjectAccessor;
                                
                                //Create (discrete index) to (game object index) mapping

                                //indexesForDistance += c * pow(100, visibleGameObjectCountMatrix);
                                //Increment this value until it becomes the maximum # of GO in matrix
                                //visibleGameObjectCountMatrix++;

                                //visibleGameObjectCountMatrix runs once for either case, but if one case runs, the other case doesn't
                                //Why the fuck does this happen?

                                visibleGameObjectCountMatrix++;
                            }
                        }
                    }
                }
            }
        }
        //End of convolution matrix processing

        //Come up with a test:
        //Test for accuracy of matrix
        // if(visibleGameObjectCountMatrix == 0) {
        //     //Dest[id.xy] = float4(1,0,0,1);
        // }

        //At this point, either emptyMatrix or we have all the information needed to apply to this pixel
        if(!emptyMatrix) {
            if(visibleGameObjectCountMatrix == 2) {
                Dest[id.xy] = float4(1,0,0,1);
            }

            //Test of whether for each xy, it properly colors the right location in StorageTextureArray
            // for(int i0 = 0; i0 < visibleGameObjectCountMatrix; i0++) {
            //     uint currentGameObjectIndex = (indexesForDistance / pow(100, i0)) % 100;

            //     //Access color
            //     int finalDeltaX = (omegaX / pow(100, currentGameObjectIndex)) % 100;
            //     int finalDeltaY = (omegaY / pow(100, currentGameObjectIndex)) % 100;
            //     float pythagoreanDelta = 1 / rsqrt(finalDeltaX * finalDeltaX + finalDeltaY * finalDeltaY);
            //     //Here, apply appropriate signs
            //         //Fetch sign for x
            //     if((((signUnsignXY / pow(100, currentGameObjectIndex)) % 100) / pow(10, 1)) % 10 == 0) {
            //         finalDeltaX = -finalDeltaX;
            //     }
            //         //Fetch sign for y
            //     if(((signUnsignXY / pow(100, currentGameObjectIndex)) % 100) % 10 == 0) {
            //         finalDeltaY = -finalDeltaY;
            //     } 
            //     float4 storageColor = StorageTextureArray.Load(uint3(id.xy + int2(finalDeltaX, finalDeltaY), currentGameObjectIndex));
            //     //This is supposed to color only the slime borders...
            //     Dest[id.xy + int2(finalDeltaX, finalDeltaY)] = float4(1,0,0,1);
            // }

            //This logic seems to be problematic ()
                //Why the fuck is the shape of all the id.xy where # of game objects found within the matrix >= 2 fucked up?

            //This logic must be true for a wide area?
            // if(visibleGameObjectCountMatrix >= 2) {
            //     Dest[id.xy] = float4(1,0,1,1);
            // }
            
            // if(visibleGameObjectCountMatrix >= 2) {
            //     //Only apply metaball effect if at least 2 game objects are within the matrix, otherwise, meaningless
            //     float avgDistanceDelta = 0;
            //     float totalDistanceDelta = 0;
            //     float4 weightedColor = float4(0,0,0,0);

            //     //Loop responsible for computing: 
            //         //1. avg distance
            //         //2. total distance
            //     for(uint i1 = 0; i1 < visibleGameObjectCountMatrix; i1++) {
            //         uint currentGameObjectIndex = (indexesForDistance / pow(100, i1)) % 100;

            //         //finalDeltaX and finalDeltaY are guaranteed to be > 0, since they were stored that way
            //         uint finalDeltaX = (omegaX / pow(100, currentGameObjectIndex)) % 100;
            //         uint finalDeltaY = (omegaY / pow(100, currentGameObjectIndex)) % 100;
            //         //pythagorean delta distance
            //         float pythagoreanDelta = 1 / rsqrt(finalDeltaX * finalDeltaX + finalDeltaY * finalDeltaY);
            //         //Avg
            //         avgDistanceDelta += pythagoreanDelta / visibleGameObjectCountMatrix;
            //         //Total
            //         totalDistanceDelta += pythagoreanDelta;
            //     }
            //     //Loop responsible computing weighted color
            //     for(uint i2 = 0; i2 < visibleGameObjectCountMatrix; i2++) {
            //         uint currentGameObjectIndex = (indexesForDistance / pow(100, i2)) % 100;

            //         //Access color
            //         int finalDeltaX = (omegaX / pow(100, currentGameObjectIndex)) % 100;
            //         int finalDeltaY = (omegaY / pow(100, currentGameObjectIndex)) % 100;
            //         float pythagoreanDelta = 1 / rsqrt(finalDeltaX * finalDeltaX + finalDeltaY * finalDeltaY);
            //         //Here, apply appropriate signs
            //             //Fetch sign for x
            //         if((((signUnsignXY / pow(100, currentGameObjectIndex)) % 100) / pow(10, 1)) % 10 == 0) {
            //             finalDeltaX *= -1;
            //         }
            //             //Fetch sign for y
            //         if(((signUnsignXY / pow(100, currentGameObjectIndex)) % 100) % 10 == 0) {
            //             finalDeltaY *= -1;
            //         }
            //         float4 unweightedColor = StorageTextureArray.Load(uint3(id.xy + int2(finalDeltaX, finalDeltaY), currentGameObjectIndex));
            //         //Why is this not contained in 
            //         //int2(finalDeltaX / 1000, finalDeltaY / 1000)
            //         //int2(finalDeltaX, finalDeltaY)
            //         //Dest[id.xy + int2(finalDeltaX, finalDeltaY)] = float4(1,0,0,1);
            //         float weight = (totalDistanceDelta - pythagoreanDelta - ((visibleGameObjectCountMatrix * totalDistanceDelta - 2 * totalDistanceDelta) / visibleGameObjectCountMatrix)) / totalDistanceDelta;
            //         weightedColor += unweightedColor * weight;
            //     }

            //     //Condition to decide whether or not to color current dest.xy SV_DispatchThreadID
            //     if(avgDistanceDelta < 20) {
            //         //Dest[id.xy] = weightedColor;
            //         //Dest[id.xy] = weightedColor;
            //     }
            //     //Dest[id.xy] = float4(0,1,0,1);
            //     //Dest[id.xy] = max(testColor0, testColor1);
            //     Dest[id.xy] = float4(1,0,0,1);
            // }
            //else if (visibleGameObjectCountMatrix < 2) {
                //Dest[id.xy] = float4(1,0,0,1);
            //}
            //Dest[id.xy] = float4(1,0,1,1);
            // if((int)visibleGameObjectCountMatrix == 2) {
            //     Dest[id.xy] = float4(1,0,1,1);
            // }
            //Dest[id.xy] = float4(1,0,0,1);
        }
        return;
    }
    //Test:
    //If storage texture array was updated properly, then 
    //float4 testColor0 = StorageTextureArray.Load(uint3(id.xy, 0));
    //float4 testColor1 = StorageTextureArray.Load(uint3(id.xy, 1));
    //Dest[id.xy] = max(testColor0, testColor1);
    //Dest[id.xy] = testColor1;
    //return;
}

여기 테스팅 단계에서 보시다시피 매트릭스 내에서 게임 오브젝트가 2개 발견된 중앙 픽셀들을 모두 빨간색으로 칠하라고 했더니 이런 이상하고 징그러운 모양이 나오네요.

스크린샷 2023-07-15 오후 9.53.43.png

저좀 도와주세요ㅠㅠ

답변 1

0

안녕하세요, 인프런 AI 인턴이에요. compute shader에서 convolution matrix를 구현하고 싶은데 코드가 이상하게 돌아간다고 하셨군요. 혹시 어떤 부분이 이상하게 돌아가는지 자세히 설명해주실 수 있을까요? 그러면 더 정확한 도움을 드릴 수 있을 것 같아요.

Wonjae Oh님의 프로필 이미지
Wonjae Oh

작성한 질문수

질문하기