1
\$\begingroup\$

I tried to create a cloud filled in the box geometry, I tried using raymarching with noise texture as well direct noise function and fbm - fbm motions is good. but cloud is not get proper result like distorted also frame rate is dropping to 10 to 14, I tried to arrange array of sphere that made to even worse app breaks because it had more than 2000 sphere shapes.

Is there any better way to create 3d cloud shape even pre-calculated is fine but rendering time should have render min with frame rate of 60. Also note scene is big space. camera is at (0, 0, 100), look at (0, 0, -1, near is 1, far is 200. and I need to place in box in position (0, -65, 0) with size (400,400, 10) that size will be decided by user for now I trying to (400, 400, 10).

Note: Camera is movable so culling the view space will not help at some point the full size will be visible

I am new to shader so help me with details and point me right direction so that I can work on this. thanks a lot.

class VRay {
public:
  texture2d<float> noiseTexture;
  float time = 0;
  float planeY = -70;
  int maxSteps = 50;
  float marchSize = 0.08;
  float noiseSize = 256.0;
  float3 sun = float3(0.0, 1.0, 0.0);
  sampler noiseSampler = sampler(filter::nearest, address::repeat);
  
  float4 textureLod( float2 uv ) {
    return noiseTexture.sample(noiseSampler, uv);
  }
  
  float noise(float3 x) {
    float3 p = floor(x);
    float3 f = fract(x);
    f = f * f * (3.0 - 2.0 * f);
    float2 uv = (p.xy + float2(37.0,239.0) * p.z) + f.xy;
    float2 tex = textureLod( (uv+0.5)/noiseSize ).yx;
    return mix( tex.x, tex.y, f.z ) * 2.0 - 1.0;
  }
  
  float fbm(float3 p) {
    float3 q = p - time * 0.5 * float3(1.0, -0.2, -1.0);
    //float g = noise(q);
    float f = 0.0;
    float scale = 0.8;
    float factor = 2.02;
    for (int i = 0; i < 6; i++) {
      f += scale * noise(q);
      q *= factor;
      factor += 0.21;
      scale *= 0.5;
    }
    return f;
  }
  
  float sdfXZPlane(float3 p, float yPos) {
    //float wave = sin(p.x * 0.01 + time) * 10.0;
    return (p.y - yPos);// - wave;
  }
  
  float sdfCircle(float3 center, float3 point, float radius) {
    float distXZ = length(point.xz - center.xz) - radius;
    // Calculate the distance in the Y direction
    float distY = point.y - center.y;
    // Combine the distances to get the signed distance function for a circle in the XZ plane
    return sqrt(distXZ * distXZ + distY * distY);
  }
  
  float unionSDF(float d1, float d2) {
    return min(d1, d2);
  }
  
  float smoothMin(float a, float b, float k) {
    float h = max(k - abs(a - b), 0.0) / k;
    return min(a, b) - h * h * h * k * (1.0 / 6.0);
  }

  float coneSDF(float3 p, float3 apex, float height, float radius, float smoothness) {
    // Translate the point to the cone's local space
    float3 localP = p - apex;
    float q = length(float2(localP.x, localP.z));
    float coneDist = max(dot(float2(radius, height), float2(q, -localP.y)), localP.y + height);
    // Smooth the top of the cone
    float topDist = length(localP) - smoothness;
    return smoothMin(coneDist, topDist, smoothness);
  }
  
  float finitePlaneSDF(float3 p, float3 center, float2 size) {
    float3 d = abs(p - center) - float3(size.x, 0.0, size.y);
    return min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0));
  }
  
  float sdfSphere(float3 p, float3 center, float radius) {
    return length(p - center) - radius;
  }
  
  float cloudShape(float3 p) {
    float result = sdfXZPlane(p, planeY);
    return result;
  }
  
  float scene(float3 p) {
    float distance = cloudShape(p);
    return -distance + fbm(p);//
  }
  
  float4 displayScene(float3 point) {
    float distance = cloudShape(point);
    float edgeWidth = 0.01; // Adjust edge width as needed
    float opacity = smoothstep(-edgeWidth, edgeWidth, distance);
    return float4(1.0, 1.0, 1.0, opacity);
  }
  
  
  float4 raymarch(float3 rayOrigin, float3 rayDirection) {
    float4 res = float4(0.0);
    if(rayDirection.y >= 0.0) {
      return res;
    }
    float depth = cloudShape(rayOrigin);
    float3 p = rayOrigin + depth * rayDirection;
    float3 sunRay = normalize(sun);
    for (int i = 0; i < maxSteps; i++) {
      float density = scene(p);
      // We only draw the density if it's greater than 0
      if (density > 0.0) {
        // Directional derivative
        // For fast diffuse lighting
        float diffuse = clamp((scene(p) - scene(p + 0.3 * sunRay)) / 0.3, 0.0, 1.0 );
        float3 lin = float3(0.60,0.60,0.75) * 1.1 + 0.8 * float3(1.0,0.6,0.3) * diffuse;
        float4 color = float4(mix(float3(1.0, 1.0, 1.0), float3(0.0, 0.0, 0.0), density), density);
        color.rgb *= lin;
        color.rgb *= color.a;
        res += color * (1.0 - res.a);
      }
      depth += max(-density, marchSize);
      p = rayOrigin + depth * rayDirection;
    }
    return float4(res.rgb, 1.0);
  }
  
};

kernel void volumetricCloudCompute(texture2d<float, access::write> output [[texture(0)]],
                                   texture2d<float> noiseTexture [[texture(1)]],
                    constant CloudMarching &cloud [[buffer(0)]],
                    uint2 gid [[thread_position_in_grid]]) {
  int width = output.get_width();
  int height = output.get_height();
  float2 resolution = float2(width, height);
  float2 uv = float2(gid) / resolution;
  uv = uv * 2.0 - 1.0;
  uv.y *= -1;
  float tiles = 1.0;
  uv = uv * tiles;
  float3 imagePoint = uv.x * cloud.rayRight + uv.y * cloud.rayUp + cloud.rayOrigin + cloud.rayFront;
  float3 rayDirection = normalize(imagePoint - cloud.rayOrigin);
  VRay ray = VRay();
  ray.time = cloud.time;
  ray.noiseTexture = noiseTexture;
  float4 result = ray.raymarch(cloud.rayOrigin, rayDirection);
  output.write(result, gid);
}



fragment float4 volumetricCloudFrag(ModelVertexOut in [[stage_in]], texture2d<float> noiseTexture [[texture(0)]], constant UruFrame &frame [[buffer(0)]]) {
  int width = frame.width;
  int height = frame.height;
  float2 resolution = float2(width, height);
  float2 uv = float2(in.position.xy) / resolution;
  uv = uv * 2.0 - 1.0;
  uv.y *= -1;
  float tiles = 1.;
  uv = uv * tiles;
  float3 imagePoint = uv.x * frame.right + uv.y * frame.up + frame.cameraPosition + frame.target;
  float3 rayDirection = normalize(imagePoint - frame.cameraPosition);
  VRay ray = VRay();
  ray.time = frame.time;
  ray.noiseTexture = noiseTexture;
  float4 result = ray.raymarch(frame.cameraPosition, rayDirection);
  return result;
}

Expected Output is enter image description here

Output that I am getting is enter image description here

Zoomed In enter image description here

\$\endgroup\$
5
  • \$\begingroup\$ You may be interested in presentations about how volumetric clouds were achieved in the Horizon Zero Dawn and Forbidden West, as well as past Q&A on the topic. \$\endgroup\$
    – DMGregory
    Commented Aug 6 at 16:42
  • \$\begingroup\$ @DMGregory Thanks will check the slides. \$\endgroup\$ Commented Aug 6 at 17:35
  • \$\begingroup\$ Might partially be an issue of scale - your actual output reads as much further away than the expected output. What does it look like if you zoom in? \$\endgroup\$
    – Pikalek
    Commented Aug 7 at 0:29
  • 1
    \$\begingroup\$ @Pikalek - I have updated the zoomed shots, Also Note - I need depth variation similar to expected not the closeup shots of expected. \$\endgroup\$ Commented Aug 7 at 4:46
  • \$\begingroup\$ Using Volumetric Raymarching with VDB or Texture Data that was able to produce any type of cloud. @DMGregory Thanks a lot. \$\endgroup\$ Commented Sep 7 at 15:32

0

You must log in to answer this question.

Browse other questions tagged .