색을 생성해보자
GPUGrassCommon.hlsl 파일을 새로 만들어서 공통 요소들을 묶고 색 함수를 추가했다.
GPUGrass.cs
public struct Constants
    {
       p///////
       
       public float4 RootColor;
       public float4 TopColor;
    }
    
    public struct Grass
    {
        ///////
        
        public uint2 ColorParams;
    }
    
    
    [SerializeField] private Color rootColor;
    [SerializeField] private Color topColor;
    
    
     _constants[0].RootColor = new float4(rootColor.r, rootColor.g , rootColor.b , rootColor.a);
     _constants[0].TopColor = new float4(topColor.r , topColor.g , topColor.b , topColor.a);
GPUGlass.compute
#include "./GPUGrassCommon.hlsl"
cbuffer Constants
{
    ///////
    
    float4 RootColor;
    float4 TopColor;
}
void Tick(const uint id : SV_DispatchThreadID)
{
    
    /////////
    uint rootColorPacked = PackColor(RootColor);
    uint topColorPacked = PackColor(TopColor);
    const uint2 colors = uint2(rootColorPacked, topColorPacked);
    
   ////////
   
    GrassBuffer.Store(mad(id, GRASS_BUFFER_SIZE, 5)<<2, asuint(bend));
    GrassBuffer.Store2(mad(id, GRASS_BUFFER_SIZE, 7)<<2, asuint(colors));
}
GPUGlassCommon.hlsl
#ifndef DEF_GPU_GRASS_COMMON
#define DEF_GPU_GRASS_COMMON
#define GRASS_BUFFER_SIZE 9
#define VERTEX_PER_BLADE 15
// 0~255 색을 가지게 하도록 하기 위해 (0~1)범위로 만드는 saturate를 이용하였다
uint PackColor(float4 color)
{
    const uint r = uint(saturate(color.x) * 255.0f) << 24;
    const uint g = uint(saturate(color.y) * 255.0f) << 16;
    const uint b = uint(saturate(color.z) * 255.0f) << 8;
    const uint a = uint(saturate(color.w) * 255.0f) << 0;
    return uint(r | g | b | a);
}
float4 UnpackColor(uint color)
{
    return float4(color >> 24 & 0xff, color >> 16 & 0xFF, color >> 8 & 0xFF, color >> 0 & 0xFF) / 255.0f; 
}
#endif

색을 지정했을 때 rgba 값이 반대로 적용되는 문제가 있었다. 패딩 문제로 의심이 되어 해결하기 위해 변수 선언 순서를 바꿨다. 그리고 공통적인건 공통파일로 옮기고 비슷한 기능을 하는 부분들은 합쳤다.
GPUGrassCommon.hlsl
#ifndef DEF_GPU_GRASS_COMMON
#define DEF_GPU_GRASS_COMMON
#define GRASS_BUFFER_SIZE 9
#define VERTEX_PER_BLADE 15
struct Grass
{
    float3 position;
    float rotation;
    float2 scale;
    float bend;
    uint2 colors;
};
RWByteAddressBuffer GrassBufferStore;
void StoreGrass(Grass grass, uint id)
{
    GrassBufferStore.Store4(mad(id, GRASS_BUFFER_SIZE, 0)<<2, asuint(float4(grass.position, grass.rotation)));
    GrassBufferStore.Store2(mad(id, GRASS_BUFFER_SIZE, 4)<<2, asuint(float2(grass.scale)));
    GrassBufferStore.Store(mad(id, GRASS_BUFFER_SIZE, 6)<<2, asuint(grass.bend));
    GrassBufferStore.Store2(mad(id, GRASS_BUFFER_SIZE, 7)<<2, asuint(grass.colors));
}
ByteAddressBuffer GrassBufferLoad;
Grass LoadGrass(uint id)
{
    Grass grass;
    grass.position = asfloat(GrassBufferLoad.Load3((mad(id, GRASS_BUFFER_SIZE,0)<<2)));
    grass.rotation = asfloat(GrassBufferLoad.Load(mad(id,GRASS_BUFFER_SIZE,3)<<2));
    grass.scale = asfloat(GrassBufferLoad.Load2(mad(id,GRASS_BUFFER_SIZE,4)<<2));
    grass.bend = asfloat(GrassBufferLoad.Load(mad(id,GRASS_BUFFER_SIZE,6)<<2));
    grass.colors = asuint(GrassBufferLoad.Load2(mad(id, GRASS_BUFFER_SIZE, 7)<<2));
    return grass;
}
uint PackColor(float4 color)
{
    const uint r = uint(saturate(color.x) * 255.0f) << 24;
    const uint g = uint(saturate(color.y) * 255.0f) << 16;
    const uint b = uint(saturate(color.z) * 255.0f) << 8;
    const uint a = uint(saturate(color.w) * 255.0f) << 0;
    return uint(r | g | b | a);
}
float4 UnpackColor(uint color)
{
    return float4(color >> 24 & 0xff, color >> 16 & 0xFF, color >> 8 & 0xFF, color >> 0 & 0xFF) / 255.0f; 
}
#endif

GPU Grass의 기즈모를 움직여도 기즈모만 움직이는 문제가 있었다. Tick() 함수에 객체의 위치값을 불러와서 적용하도록 했다.
GPUGrass.cs
private static readonly int Position = Shader.PropertyToID("Position");
private void Tick()
        {
            if (!_compute) return;
            _compute.Dispatch(_tickKernelIndex, _threadGroupCount, 1, 1);
            
            if (renderMaterial)
            {
                _renderParams.worldBounds = new Bounds(transform.position, Vector3.one * 100.0f);
                _renderParams.matProps.SetVector(Position, transform.position);
                Graphics.RenderPrimitivesIndirect(_renderParams, MeshTopology.Triangles, _commandBuffer);
            }
        }
GPUGrass.shader
Shader "Unlit/GPUGrass"
{
    Properties
    {
        Position("Position", Vector) = (0,0,0)
    }
    SubShader
    {
        Pass
        {

잔디의 모양을 sincos를 활용하여 원형으로 변경했다.
GPUGrass.compute
void Tick(const uint id : SV_DispatchThreadID)
{
    if (id >= Capacity) { return; }
    Grass grass;
    //const float x = mad(GenerateHashedRandomFloat(id + 0), ScatterRange, -ScatterRange * 0.5f);
    //const float z = mad(GenerateHashedRandomFloat(id + 2), ScatterRange, -ScatterRange * 0.5f);
    //
    const float angle = mad(GenerateHashedRandomFloat(id + 0), TWO_PI, -PI);
    const float dist = pow(GenerateHashedRandomFloat(id + 1), 5.0f) * ScatterRange;
    float2 sc;
    sincos(angle, sc.x, sc.y);
    grass.position = float3(sc.x, 0.0f, sc.y) * dist;
    
    //grass.position = float3(x, 0.0f, z);
    
    grass.rotation = mad(GenerateHashedRandomFloat(id + 3), TWO_PI, -PI);
    grass.scale.x = mad(GenerateHashedRandomFloat(id + 4), ScaleParam.x, ScaleParam.y);
    grass.scale.y = mad(GenerateHashedRandomFloat(id + 5), ScaleParam.x, ScaleParam.y);
    grass.bend = GenerateHashedRandomFloat(id + 6);
    grass.colors = uint2(PackColor(RootColor), PackColor(TopColor));
    StoreGrass(grass, id);
    
}

'공부 일지 > 그래픽스' 카테고리의 다른 글
| GPU로 풀 그리기 #5 (0) | 2025.10.14 | 
|---|---|
| GPU로 풀 그리기 #4 (0) | 2025.10.11 | 
| GPU로 풀 그리기 #3 (0) | 2025.10.05 | 
| GPU로 풀 그리기 #2 (0) | 2025.10.05 | 
| GPU로 풀 그리기 #1 (0) | 2025.10.05 | 
