1// Copyright 2009-2021 Intel Corporation 2// SPDX-License-Identifier: Apache-2.0 3 4#include "../../common/DistributedWorld.ih" 5#include "DistributedRenderer.ih" 6#include "common/Instance.ih" 7#include "common/World.ih" 8#include "math/random.ih" 9#include "math/sampling.ih" 10#include "openvkl/openvkl.isph" 11#include "render/materials/OBJ.ih" 12#include "render/scivis/surfaces.ih" 13#include "render/scivis/volumes.ih" 14#include "render/util.ih" 15#include "rkcommon/math/box.ih" 16 17struct DistributedRaycastRenderer 18{ 19 DistributedRenderer super; 20 int aoSamples; 21 float aoRadius; 22 bool shadowsEnabled; 23 float volumeSamplingRate; 24}; 25 26// The distributed raycast renderer uses its own volume interval integration 27// because we want to apply the jitter before offsetting our step size to stay 28// inside the region, not after. 29vec4f DRR_integrateVolumeInterval(const ScreenSample &sample, 30 const VolumeInterval &interval, 31 Ray &ray, 32 uniform float samplingRate) 33{ 34 VolumetricModel *varying volModel = interval.volumetricModel; 35 36 Ray transformedRay = ray; 37 transformRay(transformedRay, interval.instance->rcp_xfm); 38 39 uniform unsigned int8 intervalIteratorBuffer[VKL_MAX_INTERVAL_ITERATOR_SIZE]; 40 41 vec3f color = make_vec3f(0.f); 42 float alpha = 0.f; 43 foreach_unique (vm in volModel) { 44 Volume *uniform volume = vm->volume; 45 TransferFunction *uniform tf = vm->transferFunction; 46 47 float time = 0.5f; 48 VKLIntervalIterator intervalIterator = 49 vklInitIntervalIteratorV(vm->vklIntervalContext, 50 (varying vkl_vec3f *)&transformedRay.org, 51 (varying vkl_vec3f *)&transformedRay.dir, 52 (varying vkl_range1f *)&interval.interval, 53 &time, 54 intervalIteratorBuffer); 55 56 VKLInterval interval; 57 while (vklIterateIntervalV(intervalIterator, &interval) && alpha < 0.99f) { 58 const float nominalSamplingDt = interval.nominalDeltaT / samplingRate; 59 60 // initial sub interval, based on our renderer-defined sampling rate 61 // and the volume's nominal dt 62 box1f subInterval = make_box1f(interval.tRange.lower, 63 min(interval.tRange.lower + nominalSamplingDt, 64 interval.tRange.upper)); 65 66 while (subInterval.upper - subInterval.lower > 0.f && alpha < 0.99f) { 67 transformedRay.t0 = 68 subInterval.lower + (subInterval.upper - subInterval.lower); 69 const float dt = subInterval.upper - subInterval.lower; 70 71 // Get volume sample 72 vec3f p = transformedRay.org + transformedRay.t0 * transformedRay.dir; 73 const float sample = vklComputeSampleV( 74 volume->vklSampler, (const varying vkl_vec3f *uniform) & p); 75 76 if (!isnan(sample)) { 77 vec4f sampleColorOpacity = tf->get(tf, sample); 78 79 sampleColorOpacity.w = 1.f 80 - pow(1.f - sampleColorOpacity.w * vm->densityScale, 81 dt / interval.nominalDeltaT); 82 const float clampedOpacity = clamp(sampleColorOpacity.w); 83 color = color 84 + ((1.f - alpha) * clampedOpacity 85 * make_vec3f(sampleColorOpacity)); 86 alpha = alpha + ((1.f - alpha) * clampedOpacity); 87 } 88 89 // compute next sub interval 90 subInterval.lower = subInterval.upper; 91 subInterval.upper = 92 min(subInterval.lower + nominalSamplingDt, interval.tRange.upper); 93 } 94 } 95 } 96 97 return make_vec4f(color, alpha); 98} 99 100inline float computeAO(const DistributedRaycastRenderer *uniform self, 101 const FrameBuffer *uniform fb, 102 const World *uniform world, 103 const varying vec3i &sampleID, 104 const varying DifferentialGeometry &dg) 105{ 106 const uniform int &sampleCnt = self->aoSamples; 107 const uniform int accumID = reduce_max(sampleID.z) * sampleCnt; 108 109 // init TEA RNG // 110 RandomTEA rng_state; 111 varying RandomTEA *const uniform rng = &rng_state; 112 RandomTEA__Constructor(rng, 0x290374, (fb->size.x * sampleID.y) + sampleID.x); 113 const vec2f rot = RandomTEA__getFloats(rng); 114 115 int hits = 0; 116 117 const linear3f localToWorld = frame(dg.Ns); 118 119 for (uniform int i = 0; i < sampleCnt; i++) { 120 const vec2f halton = HaltonSequence_get2D(accumID + i); 121 const vec2f r = CranleyPattersonRotation(halton, rot); 122 const vec3f local_ao_dir = cosineSampleHemisphere(r); 123 const vec3f ao_dir = localToWorld * local_ao_dir; 124 125 if (dot(ao_dir, dg.Ns) < 0.05f) { // check below surface 126 hits++; 127 continue; 128 } 129 130 Ray ao_ray; 131 setRay(ao_ray, dg.P, ao_dir, dg.epsilon, self->aoRadius); 132 if (isOccluded(world, ao_ray)) 133 hits++; 134 } 135 136 // the cosTheta of cosineSampleHemispherePDF and dot(shadingNormal, ao_dir) 137 // cancel 138 return 1.0f - (hits / (float)sampleCnt); 139} 140 141vec4f DRR_shadeSurface(const DistributedRaycastRenderer *uniform self, 142 const FrameBuffer *uniform fb, 143 const DistributedWorld *uniform world, 144 const vec3i &sampleID, 145 const Ray &ray, 146 const DifferentialGeometry &dg) 147{ 148 // TODO: DRR should have its own support for OBJ material and lighting model 149 150 vec3f surfaceColor = make_vec3f(dg.color); 151 float opacity = 1.f; 152 const OBJ *mat = (const OBJ *)dg.material; 153 if (mat) { 154 foreach_unique (m in mat) { 155 surfaceColor = m->Kd; 156 opacity = m->d; 157 if (valid(m->KdMap)) { 158 vec4f kdFromMap = get4f(m->KdMap, dg); 159 surfaceColor = surfaceColor * make_vec3f(kdFromMap); 160 } 161 } 162 } 163 164 // Just eyelight for now... 165 const float eyeLightIntensity = absf(dot(dg.Ns, ray.dir)); 166 vec3f color = surfaceColor * eyeLightIntensity; 167 if (self->aoSamples > 0) { 168 float ao = computeAO(self, fb, &world->super, sampleID, dg); 169 color = color * ao; 170 } 171 return make_vec4f(color, opacity); 172 173#if 0 174 // TODO LIGHTS 175 // Calculate shading for all lights 176 for (uniform int i = 0; self->lights && i < self->numLights; i++) { 177 const uniform Light *uniform l = self->lights[i]; 178 const vec2f s = make_vec2f(0.0f); // sample center of area lights 179 const Light_SampleRes light = l->sample(l, dg, s); 180 181 // any potential contribution? 182 if (reduce_max(light.weight) > 0.f) { 183 float cosNL = dot(light.dir, info.shadingNormal); 184 if (cosNL < 0.0f) 185 continue; 186 187 cosNL = abs(cosNL); 188 189 const vec3f H = normalize(light.dir - ray.dir); 190 const float cosNH = dot(H, info.shadingNormal); 191 const vec3f specular = info.Ks * powf(cosNH, info.Ns); 192 const vec3f diffuse = info.Kd * cosNL; 193 const vec3f light_contrib = 194 info.local_opacity * (diffuse + specular) * light.weight; 195 196 if (self->shadowsEnabled) { 197 const float max_contrib = reduce_max(light_contrib); 198 if (max_contrib > self->super.minContribution) { 199 vec3f P = dg.P; 200 if (dot(dg.Ng, light.dir) < 0.0f) { 201 P = P - (2.f * dg.epsilon) * dg.Ng; 202 } 203 204 Ray shadowRay; 205 setRay(shadowRay, P, light.dir, 0.0f, light.dist, 0.5f); 206 if (!isOccluded(model, shadowRay) 207 && !(ghostModel && isOccluded(ghostModel, shadowRay))) 208 { 209 color = color + light_contrib; 210 } 211 } 212 } else { 213 color = color + light_contrib; 214 } 215 } 216 } 217#endif 218 return color; 219} 220 221void DRR_renderRegionSample(DistributedRenderer *uniform _self, 222 FrameBuffer *uniform fb, 223 DistributedWorld *uniform world, 224 const box3f *uniform region, 225 const vec2f ®ionInterval, 226 void *uniform perFrameData, 227 varying ScreenSample &sample) 228{ 229 DistributedRaycastRenderer *uniform self = 230 (DistributedRaycastRenderer * uniform) _self; 231 232 Ray &geomRay = sample.ray; 233 Ray volumeRay = sample.ray; 234 235 VolumeInterval volumeInterval; 236 237 traceRay(&world->super, geomRay); 238 traceVolumeRay(&world->super, volumeRay, volumeInterval); 239 volumeInterval.interval.lower = 240 max(volumeInterval.interval.lower, regionInterval.x); 241 volumeInterval.interval.upper = 242 min(volumeInterval.interval.upper, regionInterval.y); 243 244 sample.z = min(geomRay.t, volumeInterval.interval.lower); 245 246 // TODO: Doesn't seem like much jittering happens with the volume integration 247 vec4f outputColor = make_vec4f(0.f); 248 while (outputColor.w < 0.99f) { 249 const bool haveGeometryHit = hadHit(geomRay) 250 && geomRay.t >= regionInterval.x && geomRay.t <= regionInterval.y; 251 252 const bool haveVolumeHit = hasInterval(volumeInterval); 253 254 const bool bothHit = haveGeometryHit && haveVolumeHit; 255 const bool eitherHit = haveGeometryHit || haveVolumeHit; 256 257 const bool volumeFirst = volumeInterval.interval.lower < geomRay.t; 258 259 if (!eitherHit) { 260 break; 261 } 262 263 vec4f volumeColor = make_vec4f(0.f); 264 vec4f surfaceColor = make_vec4f(0.f); 265 DifferentialGeometry dg; 266 if (haveGeometryHit) { 267 postIntersect(&world->super, 268 &self->super.super, 269 dg, 270 geomRay, 271 DG_NG | DG_NS | DG_NORMALIZE | DG_FACEFORWARD | DG_COLOR 272 | DG_TEXCOORD); 273 surfaceColor = 274 DRR_shadeSurface(self, fb, world, sample.sampleID, geomRay, dg); 275 } 276 277 // Always just integrate the volume when it comes in front of the geometry 278 if (haveVolumeHit && volumeFirst) { 279 volumeInterval.interval.upper = 280 min(geomRay.t, volumeInterval.interval.upper); 281 282 // Keep the interval within the region 283 volumeInterval.interval.lower = 284 max(volumeInterval.interval.lower, regionInterval.x); 285 volumeInterval.interval.upper = 286 min(volumeInterval.interval.upper, regionInterval.y); 287 288 // Offset the volume interval start to align to the same sampling steps 289 // across each rank, and apply the same jitter for each pixel on all ranks 290 float dt = 1.f /*was 'volModel->samplingStep*/ / self->volumeSamplingRate; 291 float t0 = volumeInterval.interval.lower; 292 volumeInterval.interval.lower = floor(t0 / dt) * dt; 293 294 float jitter = Halton_sample2(sample.sampleID.z); 295 int ix = sample.sampleID.x % 4; 296 int iy = sample.sampleID.y % 4; 297 298 int patternID = ix + 4 * iy; 299 jitter += Halton_sample3(patternID); 300 if (jitter > 1.f) { 301 jitter -= 1.f; 302 } 303 volumeInterval.interval.lower += jitter * dt; 304 305 if (volumeInterval.interval.lower < t0) { 306 volumeInterval.interval.lower += dt; 307 } 308 309 // TODO: At edges when coloring all bricks the same I do still see 310 // some edge/border artifacts 311 312 setRay(volumeRay, 313 volumeInterval.interval.lower, 314 volumeInterval.interval.upper); 315 volumeColor = DRR_integrateVolumeInterval( 316 sample, volumeInterval, volumeRay, self->volumeSamplingRate); 317 } 318 319 vec4f blendedColor = make_vec4f(0.f); 320 if (bothHit) { 321 blendedColor = volumeColor + (1.0f - volumeColor.w) * surfaceColor; 322 } else { 323 if (haveGeometryHit) 324 blendedColor = surfaceColor; 325 else 326 blendedColor = volumeColor; 327 } 328 329 outputColor = outputColor + (1.0f - outputColor.w) * blendedColor; 330 331 if (haveGeometryHit) 332 setRay(geomRay, geomRay.t + dg.epsilon, regionInterval.y + 0.0001); 333 else 334 setRay(geomRay, geomRay.t, geomRay.t0); 335 336 // Step the volume ray forwards as well 337 volumeRay = geomRay; 338 339 traceRay(&world->super, geomRay); 340 traceVolumeRay(&world->super, volumeRay, volumeInterval); 341 } 342 sample.rgb = make_vec3f(outputColor); 343 sample.alpha = outputColor.w; 344} 345 346// Exported functions ///////////////////////////////////////////////////////// 347 348export void *uniform DistributedRaycastRenderer_create() 349{ 350 DistributedRaycastRenderer *uniform self = 351 uniform new uniform DistributedRaycastRenderer; 352 353 DistributedRenderer_Constructor(&self->super); 354 self->super.renderRegionSample = DRR_renderRegionSample; 355 self->aoSamples = 0; 356 self->shadowsEnabled = false; 357 358 return self; 359} 360 361export void DistributedRaycastRenderer_set(void *uniform _self, 362 const uniform int aoSamples, 363 const uniform float aoRadius, 364 const uniform bool shadowsEnabled, 365 const uniform float volumeSamplingRate) 366{ 367 DistributedRaycastRenderer *uniform self = 368 (DistributedRaycastRenderer * uniform) _self; 369 370 self->aoSamples = aoSamples; 371 self->aoRadius = aoRadius; 372 self->shadowsEnabled = shadowsEnabled; 373 self->volumeSamplingRate = volumeSamplingRate; 374} 375