prefilterEquirectRadiance function
Prefilters an equirectangular radiance texture into a vertical roughness-band atlas for image-based specular lighting.
Renders kPrefilterBandCount GGX-prefiltered equirectangular bands
stacked vertically into a single r16g16b16a16Float texture in one
full-screen GPU pass (see flutter_scene_prefilter_env.frag). Intended
to run once when an EnvironmentMap is constructed; the result is cached
on the environment and sampled at draw time by the standard shader's
SamplePrefilteredRadiance.
sourceEquirect is an equirectangular radiance map. By default it is
treated as sRGB-encoded; pass sourceIsLinear when it already holds
linear radiance (an HDR environment), so it is not linearized twice.
The atlas always stores linear radiance.
Implementation
gpu.Texture prefilterEquirectRadiance(
gpu.Texture sourceEquirect, {
bool sourceIsLinear = false,
}) {
final atlas = gpu.gpuContext.createTexture(
gpu.StorageMode.devicePrivate,
kPrefilterBandWidth,
kPrefilterBandHeight * kPrefilterBandCount,
format: gpu.PixelFormat.r16g16b16a16Float,
enableRenderTargetUsage: true,
enableShaderReadUsage: true,
coordinateSystem: gpu.TextureCoordinateSystem.renderToTexture,
);
final fragmentShader = baseShaderLibrary['PrefilterEnvFragment']!;
final commandBuffer = gpu.gpuContext.createCommandBuffer();
final renderPass = commandBuffer.createRenderPass(
gpu.RenderTarget.singleColor(
gpu.ColorAttachment(texture: atlas, clearValue: Vector4.zero()),
),
);
renderPass.bindPipeline(
gpu.gpuContext.createRenderPipeline(
baseShaderLibrary['FullscreenVertex']!,
fragmentShader,
),
);
renderPass.bindVertexBuffer(_fullscreenQuadView, 6);
renderPass.bindTexture(
fragmentShader.getUniformSlot('source_equirect'),
sourceEquirect,
sampler: gpu.SamplerOptions(
minFilter: gpu.MinMagFilter.linear,
magFilter: gpu.MinMagFilter.linear,
widthAddressMode: gpu.SamplerAddressMode.repeat,
heightAddressMode: gpu.SamplerAddressMode.clampToEdge,
),
);
// A single float (std140-padded to 16 bytes): the sRGB-vs-linear flag.
final info = Float32List(4)..[0] = sourceIsLinear ? 1.0 : 0.0;
renderPass.bindUniform(
fragmentShader.getUniformSlot('PrefilterInfo'),
gpu.gpuContext.createHostBuffer().emplace(ByteData.sublistView(info)),
);
renderPass.draw();
commandBuffer.submit();
return atlas;
}