最近写GPU画刷的时候,发现存入贴图的值经常会限制在某一个值不动,不会继续升高或者降低,像是精度丢失了一样

CS的操作为对一张目标格式为RGBA8_unorm的贴图的某一个通道不停加上一个step值,然后再对四个通道做加权归一化,将归一化后的值写回贴图中

简单CS代码如下

RWTexture2D<float4> CanvasMap;

// Main draw function
[numthreads(8, 8, 1)]
void Draw(uint2 dispatchThreadId : SV_DispatchThreadID)
{
    if (dispatchThreadId.x >= BrushSize || dispatchThreadId.y >= BrushSize)
        return;

    ......

    uint2 position = uint2(uint(floor(startX)), uint(floor(startZ))) + dispatchThreadId;

    // calculate influence
    const float4 coordinate = GetCoordinate(position, worldPosition, canvasScale);
    float influence = GetInfluence((coordinate - BrushPosition).xz);

    // Get current value in canvas map
    float currentValue = ReadTexturePixel(uint3(position, depth));
    float targetValue = GetTargetValue(position);

    // current frame time(ms) pre dispatch
    float step = (targetValue - currentValue) * Strength * influence * FrameRate;
    // write new value to texture
    WriteTexturePixel(uint3(position, depth), clamp(currentValue + step, 0.0f, 1.0f));
}

这个是对某个通道的值进行步进累加的操作,后续的normalize操作没在这边写出来
不过就两个通道有值的情况下norimalize后的值会锁定在一个极限

值不会锁定在0或者1这个很奇怪,理论上一个通道能写入255个数值,值域为[0.0f, 1.0f],极限写入至少应该能写到0或者写到1这样子
在通过CS不停写入贴图的时候,通过截帧发现这个值会固定在0.04314这个附近,对应的阶梯值为 11 / 255,也就是说后续的值写入都失败了

我们假定R通道为0.04314,G通道为0.95686,B通道和A通道为0
现在对R通道步进一个很小的值0.01,现在R通道的值为0.05314,G通道为0.95686

开始归一化
R通道计算结果为0.04314 / (1 + 0.01) = 0.04271
G通道计算结果为0.95686 / (1 + 0.01) = 0.94739

我们发现 0.04314 * 255 = 11.00070.04271 * 255 = 10.89105
将这两个值写入Texture2D<float4>时会自动转换为uint8类型,在CPU端,这个转换逻辑为static_cast<unsigned char>(),然而在GPU端,这个逻辑通过实验发现应该为round()

因此为了保证值都能写入正确,GPU端对写入的值应该执行 floor(x * 255) / 255.0f;这样一个操作,保证值能够覆盖全值域范围。

因此我们也可以看出GPU端CS写入贴图的时候会自动将值round到最近的unorm float值上
对于需要覆盖全[0, 255]这个范围的写入操作,需要自己进行一次floor()操作保证正确性

说点什么
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...