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;
}