风格化卡通体积云

风格化卡通体积云

function getCloudColor(
    viewVector,      // 视线方向
    skyColor,        // 背景天空色
    basePos,         // 射线起点
    samples,         // 采样次数
    umbral,          // 阈值,用于剔除低噪声
    brightFactor,    // 高光强度
    dither,          // 抖动参数
    CLOUD_PARAMS     // 各种云层平面、中心、厚度等常量
):
    // 1. 如果视线朝下,直接返回天空色
    if viewVector.y0:
        return skyColor

    // 2. 计算射线与上下云层平面的交点 t0、t1
    t0 = (CLOUD_BOTTOM - basePos.y) / viewVector.y
    t1 = (CLOUD_TOP    - basePos.y) / viewVector.y

    // 3. 生成采样起点 p 和步长 stepV
    p     = basePos + viewVector * t0
    pEnd  = basePos + viewVector * t1
    stepV = (pEnd - p) / samples
    p    += stepV * dither  // 加入少量抖动,减少条带

    // 4. 沿射线采样,累积“云厚度” cv 和“首次击中深度” den
    cv       = 0          // 累计的云体积分量
    den      = 0          // 首次进入云层的相对位置(0~1)
    firstHit = true
    totalRange = (CLOUD_CENTER - CLOUD_BOTTOM) + (CLOUD_TOP - CLOUD_CENTER)

    for i in 0 .. samples-1:
        // 4.1 采样噪声(height field)
        noiseHi = sampleNoiseHeight(p.xz, timeOffsetHigh)
        noiseLo = sampleNoiseHeight(p.zx, timeOffsetLow)  // 可选多层混合
        noise   = mixAndSmooth(noiseHi, noiseLo)

        // 4.2 根据阈值计算云层上下边界
        v    = (noise - umbral) / (1 - umbral)
        inf  = CLOUD_CENTER - v * (CLOUD_CENTER - CLOUD_BOTTOM)
        sup  = CLOUD_CENTER + v * (CLOUD_TOP    - CLOUD_CENTER)

        // 4.3 判断当前采样点 p.y 是否在云层内部
        if inf < p.y < sup:
            cv += min(stepLength, sup - inf)
            if firstHit:
                den      = (sup - p.y) / totalRange
                firstHit = false
        else if withinSoftEdge(p.y, inf, sup, CLOUD_EDGE_WIDTH):
            // 边缘过渡:软添加一点积累
            cv += softBlendAmount(p.y, inf, sup) 
            if firstHit:
                den      = (sup - p.y) / totalRange
                firstHit = false

        p += stepV

    // 5. 归一化累积值和深度
    opacity   = clamp(cv / (2 * CLOUD_EDGE_WIDTH / viewVector.y), 0, 1)
    density   = clamp(den, 0.0001, 1)

    // 6. 按 density 分层选色:低—中—高
    if density < 0.33:
        baseColor = COL_SH  // 云底色
    else if density < 0.66:
        baseColor = COL_MD  // 中层色
    else:
        baseColor = COL_HI  // 云顶色

    // 7. 视向高光:只有高密度部分才额外提亮
    if density > 0.66:
        highlight = dot(computeNormal(density), -viewVector)
        baseColor = mix(baseColor, COL_HI, highlight * brightFactor)

    // 8. 自阴影:越厚越暗
    baseColor *= mix(1.0, 0.85, density^2 * 0.5)

    // 9. 边缘描边:根据深度场梯度增强轮廓
    edgeFactor = computeEdgeFactor(opacity)
    baseColor  = mix(baseColor, OUTLINE_COLOR, edgeFactor * 0.3)

    // 10. 根据天空亮度在夜间适当暗化
    nightFactor = computeNightFactor(skyColor)
    baseColor  *= nightFactor

    // 11. 最终混合:云层覆盖背景
    alpha = opacity * clamp((viewVector.y - 0.05) * 5.0, 0, 1)
    return mix(skyColor, baseColor, alpha)
JavaScript

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理

zh_CNCN