UNPKG

@cesium/engine

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

179 lines (152 loc) 6.35 kB
import defined from "../../Core/defined.js"; import Cartesian4 from "../../Core/Cartesian4.js"; import ShaderDestination from "../../Renderer/ShaderDestination.js"; import ModelUtility from "./ModelUtility.js"; import VertexAttributeSemantic from "../VertexAttributeSemantic.js"; /** * The dequantization stage generates shader code to dequantize attributes * in the vertex shader * * @namespace DequantizationPipelineStage * * @private */ const DequantizationPipelineStage = { name: "DequantizationPipelineStage", // Helps with debugging FUNCTION_ID_DEQUANTIZATION_STAGE_VS: "dequantizationStage", FUNCTION_SIGNATURE_DEQUANTIZATION_STAGE_VS: "void dequantizationStage(inout ProcessedAttributes attributes)", }; /** * Process a primitive with quantized attributes. This stage modifies the * following parts of the render resources: * <ul> * <li>generates dequantization function and adds it to the shader</li> * <li>adds any uniforms needed for dequantization to the shader and uniform map</li> * </ul> * * @param {PrimitiveRenderResources} renderResources The render resources for this primitive. * @param {ModelComponents.Primitive} primitive The primitive * @param {FrameState} frameState The frame state. * * @private */ DequantizationPipelineStage.process = function ( renderResources, primitive, frameState, ) { const shaderBuilder = renderResources.shaderBuilder; const model = renderResources.model; const hasClassification = defined(model.classificationType); shaderBuilder.addDefine( "USE_DEQUANTIZATION", undefined, ShaderDestination.VERTEX, ); shaderBuilder.addFunction( DequantizationPipelineStage.FUNCTION_ID_DEQUANTIZATION_STAGE_VS, DequantizationPipelineStage.FUNCTION_SIGNATURE_DEQUANTIZATION_STAGE_VS, ShaderDestination.VERTEX, ); const attributes = primitive.attributes; for (let i = 0; i < attributes.length; i++) { const attribute = attributes[i]; const quantization = attribute.quantization; if (!defined(quantization)) { // Non-quantized attributes were already handled in GeometryPipelineStage continue; } // Only the position and texcoord attributes are used for classification models. const isPositionAttribute = attribute.semantic === VertexAttributeSemantic.POSITION; const isTexcoordAttribute = attribute.semantic === VertexAttributeSemantic.TEXCOORD; if (hasClassification && !isPositionAttribute && !isTexcoordAttribute) { continue; } const attributeInfo = ModelUtility.getAttributeInfo(attribute); updateDequantizationFunction(shaderBuilder, attributeInfo); addDequantizationUniforms(renderResources, attributeInfo); } }; function addDequantizationUniforms(renderResources, attributeInfo) { const shaderBuilder = renderResources.shaderBuilder; const uniformMap = renderResources.uniformMap; const variableName = attributeInfo.variableName; const quantization = attributeInfo.attribute.quantization; if (quantization.octEncoded) { const normalizationRange = `model_normalizationRange_${variableName}`; shaderBuilder.addUniform( "float", normalizationRange, ShaderDestination.VERTEX, ); uniformMap[normalizationRange] = function () { return quantization.normalizationRange; }; } else { const offset = `model_quantizedVolumeOffset_${variableName}`; const stepSize = `model_quantizedVolumeStepSize_${variableName}`; const glslType = attributeInfo.glslType; shaderBuilder.addUniform(glslType, offset, ShaderDestination.VERTEX); shaderBuilder.addUniform(glslType, stepSize, ShaderDestination.VERTEX); let quantizedVolumeOffset = quantization.quantizedVolumeOffset; let quantizedVolumeStepSize = quantization.quantizedVolumeStepSize; // COLOR_n is promoted to a vec4 in the shader, so the alpha value // defaults to 1. For correctness, the quantization uniforms must be // promoted to vec4s. The alpha values are chosen so the alpha // dequantization is the identity, i.e. 0.0 + 1.0 * color.a if (/^color_\d+$/.test(variableName)) { quantizedVolumeOffset = promoteToVec4(quantizedVolumeOffset, 0); quantizedVolumeStepSize = promoteToVec4(quantizedVolumeStepSize, 1); } uniformMap[offset] = function () { return quantizedVolumeOffset; }; uniformMap[stepSize] = function () { return quantizedVolumeStepSize; }; } } function promoteToVec4(value, defaultAlpha) { if (value instanceof Cartesian4) { return value; } return new Cartesian4(value.x, value.y, value.z, defaultAlpha); } function updateDequantizationFunction(shaderBuilder, attributeInfo) { const variableName = attributeInfo.variableName; const quantization = attributeInfo.attribute.quantization; let line; if (quantization.octEncoded) { line = generateOctDecodeLine(variableName, quantization); } else { line = generateDequantizeLine(variableName); } shaderBuilder.addFunctionLines( DequantizationPipelineStage.FUNCTION_ID_DEQUANTIZATION_STAGE_VS, [line], ); } function generateOctDecodeLine(variableName, quantization) { const structField = `attributes.${variableName}`; const quantizedAttribute = `a_quantized_${variableName}`; const normalizationRange = `model_normalizationRange_${variableName}`; // Draco stores things as .zxy instead of xyz, so be explicit about the // swizzle to avoid confusion const swizzle = quantization.octEncodedZXY ? ".zxy" : ".xyz"; // This generates lines such as: // attributes.normal = czm_octDecode(a_quantized_normal, model_normalizationRange_normal).zxy; return `${structField} = czm_octDecode(${quantizedAttribute}, ${normalizationRange})${swizzle};`; } function generateDequantizeLine(variableName) { const structField = `attributes.${variableName}`; const quantizedAttribute = `a_quantized_${variableName}`; const offset = `model_quantizedVolumeOffset_${variableName}`; const stepSize = `model_quantizedVolumeStepSize_${variableName}`; // This generates lines such as: // attributes.texCoord_0 = model_quantizedVolumeOffset_texCoord_0 + a_quantized_texCoord_0 * model_quantizedVolumeStepSize; return `${structField} = ${offset} + ${quantizedAttribute} * ${stepSize};`; } export default DequantizationPipelineStage;