색을 생성해보자
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);
}

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