|
9 | 9 | #ifdef PBRT_BUILD_GPU_RENDERER |
10 | 10 | #ifndef __HIP_PLATFORM_AMD__ |
11 | 11 | #include <pbrt/gpu/optix/denoiser.h> |
| 12 | +#include <pbrt/gpu/optix/scaler.h> |
12 | 13 | #endif // __HIP_PLATFORM_AMD__ |
13 | 14 | #include <pbrt/gpu/util.h> |
14 | 15 | #endif // PBRT_BUILD_GPU_RENDERER |
@@ -149,6 +150,14 @@ static std::map<std::string, CommandUsage> commandUsage = { |
149 | 150 | " be a multi-channel EXR as generated by pbrt's \"gbuffer\" film.", |
150 | 151 | std::string(R"( options: |
151 | 152 | --outfile <name> Filename to use for the denoised image. |
| 153 | +)")}}, |
| 154 | + {"scale-optix", |
| 155 | + {"scale-optix [options] <filename>", |
| 156 | + "2X Scales the image using NVIDIA's OptiX denoiser which is\n" |
| 157 | + " based on a deep neural network. The provided image should\n" |
| 158 | + " be a multi-channel EXR as generated by pbrt's \"gbuffer\" film.", |
| 159 | + std::string(R"( options: |
| 160 | + --outfile <name> Filename to use for the scaled image. |
152 | 161 | )")}}, |
153 | 162 | #endif // PBRT_BUILD_GPU_RENDERER |
154 | 163 | {"error", |
@@ -222,7 +231,7 @@ static std::map<std::string, CommandUsage> commandUsage = { |
222 | 231 | {"scalenormalmap", |
223 | 232 | {"scalenormalmap [options] <filename>", |
224 | 233 | "Scale the provided normal map by applying the given factor for x and y\n" |
225 | | - " and output the resulting normal map.\n", |
| 234 | + " and output the resulting normal map.", |
226 | 235 | std::string(R"( |
227 | 236 | --scale <s> Scale factor. Default: 1 |
228 | 237 | --outfile <name> Filename to store final image in. |
@@ -2335,6 +2344,125 @@ int denoise_optix(std::vector<std::string> args) { |
2335 | 2344 |
|
2336 | 2345 | return 0; |
2337 | 2346 | } |
| 2347 | + |
| 2348 | +int scale_optix(std::vector<std::string> args) { |
| 2349 | + std::string inFilename, outFilename; |
| 2350 | + |
| 2351 | + auto onError = [](const std::string &err) { |
| 2352 | + usage("scale-optix", "%s", err.c_str()); |
| 2353 | + exit(1); |
| 2354 | + }; |
| 2355 | + for (auto iter = args.begin(); iter != args.end(); ++iter) { |
| 2356 | + if (ParseArg(&iter, args.end(), "outfile", &outFilename, onError)) { |
| 2357 | + // success |
| 2358 | + } else if ((*iter)[0] == '-') |
| 2359 | + usage("scale-optix", "%s: unknown command flag", iter->c_str()); |
| 2360 | + else if (inFilename.empty()) { |
| 2361 | + inFilename = *iter; |
| 2362 | + } else |
| 2363 | + usage("scale-optix", "multiple input filenames provided."); |
| 2364 | + } |
| 2365 | + if (inFilename.empty()) |
| 2366 | + usage("scale-optix", "input image filename must be provided."); |
| 2367 | + if (outFilename.empty()) |
| 2368 | + usage("scale-optix", "output image filename must be provided."); |
| 2369 | + |
| 2370 | + ImageAndMetadata im = Image::Read(inFilename); |
| 2371 | + Image &image = im.image; |
| 2372 | + |
| 2373 | + CUDA_CHECK(cudaFree(nullptr)); |
| 2374 | + |
| 2375 | + int nLayers = 3; |
| 2376 | + bool oldNormalNaming = false; |
| 2377 | + ImageChannelDesc desc[3] = { |
| 2378 | + image.GetChannelDesc({"R", "G", "B"}), |
| 2379 | + image.GetChannelDesc({"Albedo.R", "Albedo.G", "Albedo.B"}), |
| 2380 | + image.GetChannelDesc({"Ns.X", "Ns.Y", "Ns.Z"})}; |
| 2381 | + if (!desc[0]) { |
| 2382 | + Error("%s: image doesn't have R, G, B channels.", inFilename); |
| 2383 | + return 1; |
| 2384 | + } |
| 2385 | + if (!desc[1]) { |
| 2386 | + Warning("%s: image doesn't have Albedo.{R,G,B} channels. " |
| 2387 | + "Denoising quality may suffer.", |
| 2388 | + inFilename); |
| 2389 | + nLayers = 1; |
| 2390 | + } |
| 2391 | + if (!desc[2]) { |
| 2392 | + // Try the old naming scheme |
| 2393 | + desc[2] = image.GetChannelDesc({"Nsx", "Nsy", "Nsz"}); |
| 2394 | + if (desc[2]) |
| 2395 | + oldNormalNaming = true; |
| 2396 | + else { |
| 2397 | + Warning("%s: image doesn't have Ns.X, Ns.Y, Ns.Z channels. " |
| 2398 | + "Denoising quality may suffer.", |
| 2399 | + inFilename); |
| 2400 | + nLayers = 1; |
| 2401 | + } |
| 2402 | + } |
| 2403 | + |
| 2404 | + Scaler scaler((Vector2i)image.Resolution(), nLayers == 3); |
| 2405 | + |
| 2406 | + size_t imageBytes = 3 * image.Resolution().x * image.Resolution().y * sizeof(float); |
| 2407 | + |
| 2408 | + auto copyChannelsToGPU = [&](std::array<std::string, 3> ch, bool flipZ = false) { |
| 2409 | + void *bufGPU; |
| 2410 | + CUDA_CHECK(cudaMalloc(&bufGPU, imageBytes)); |
| 2411 | + std::vector<float> hostStaging(imageBytes / sizeof(float)); |
| 2412 | + |
| 2413 | + ImageChannelDesc desc = image.GetChannelDesc(ch); |
| 2414 | + CHECK(desc); |
| 2415 | + int offset = 0; |
| 2416 | + for (int y = 0; y < image.Resolution().y; ++y) |
| 2417 | + for (int x = 0; x < image.Resolution().x; ++x) { |
| 2418 | + ImageChannelValues v = image.GetChannels({x, y}, desc); |
| 2419 | + if (flipZ) |
| 2420 | + v[2] *= -1; // flip normal's z--right handed... |
| 2421 | + for (int c = 0; c < 3; ++c) |
| 2422 | + hostStaging[offset++] = v[c]; |
| 2423 | + } |
| 2424 | + CUDA_CHECK( |
| 2425 | + cudaMemcpy(bufGPU, hostStaging.data(), imageBytes, cudaMemcpyHostToDevice)); |
| 2426 | + return bufGPU; |
| 2427 | + }; |
| 2428 | + RGB *rgbGPU = (RGB *)copyChannelsToGPU({"R", "G", "B"}); |
| 2429 | + |
| 2430 | + RGB *albedoGPU = nullptr; |
| 2431 | + Normal3f *normalGPU = nullptr; |
| 2432 | + if (nLayers == 3) { |
| 2433 | + albedoGPU = (RGB *)copyChannelsToGPU({"Albedo.R", "Albedo.G", "Albedo.B"}); |
| 2434 | + if (oldNormalNaming) |
| 2435 | + normalGPU = (Normal3f *)copyChannelsToGPU({"Nsx", "Nsy", "Nsz"}, true); |
| 2436 | + else |
| 2437 | + normalGPU = (Normal3f *)copyChannelsToGPU({"Ns.X", "Ns.Y", "Ns.Z"}, true); |
| 2438 | + } |
| 2439 | + |
| 2440 | + RGB *rgbResultGPU; |
| 2441 | + Point2i destRes(image.Resolution().x * 2, image.Resolution().y * 2); |
| 2442 | + size_t destBytes = 3 * destRes.x * destRes.y * sizeof(float); |
| 2443 | + CUDA_CHECK(cudaMalloc(&rgbResultGPU, destBytes)); |
| 2444 | + |
| 2445 | + scaler.Scale(rgbGPU, normalGPU, albedoGPU, rgbResultGPU); |
| 2446 | + |
| 2447 | + CUDA_CHECK(cudaDeviceSynchronize()); |
| 2448 | + |
| 2449 | + Image result(PixelFormat::Float, destRes, {"R", "G", "B"}); |
| 2450 | + CUDA_CHECK(cudaMemcpy(result.RawPointer({0, 0}), (const void *)rgbResultGPU, |
| 2451 | + destBytes, cudaMemcpyDeviceToHost)); |
| 2452 | + |
| 2453 | + ImageMetadata outMetadata; |
| 2454 | + outMetadata.cameraFromWorld = im.metadata.cameraFromWorld; |
| 2455 | + outMetadata.NDCFromWorld = im.metadata.NDCFromWorld; |
| 2456 | + outMetadata.pixelBounds = Bounds2i( |
| 2457 | + {im.metadata.pixelBounds->pMin.x * 2, im.metadata.pixelBounds->pMin.y * 2}, |
| 2458 | + {im.metadata.pixelBounds->pMax.x * 2, im.metadata.pixelBounds->pMax.y * 2}); |
| 2459 | + outMetadata.fullResolution = destRes; |
| 2460 | + outMetadata.colorSpace = im.metadata.colorSpace; |
| 2461 | + |
| 2462 | + CHECK(result.Write(outFilename, outMetadata)); |
| 2463 | + |
| 2464 | + return 0; |
| 2465 | +} |
2338 | 2466 | #endif // PBRT_BUILD_GPU_RENDERER |
2339 | 2467 |
|
2340 | 2468 | int main(int argc, char *argv[]) { |
@@ -2365,6 +2493,8 @@ int main(int argc, char *argv[]) { |
2365 | 2493 | #ifdef PBRT_BUILD_GPU_RENDERER |
2366 | 2494 | else if (cmd == "denoise-optix") |
2367 | 2495 | return denoise_optix(args); |
| 2496 | + else if (cmd == "scale-optix") |
| 2497 | + return scale_optix(args); |
2368 | 2498 | #endif // PBRT_BUILD_GPU_RENDERER |
2369 | 2499 | else if (cmd == "error") |
2370 | 2500 | return error(args); |
|
0 commit comments