Dies ist das Repository meines kleinen Portfolios.
Im Hintergrund läuft eine Planetensimulation, geschrieben in JavaScript und Three.js.
Die zu sehenden Texturen stammen von:
https://www.solarsystemscope.com/textures/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
737 lines
16 KiB
737 lines
16 KiB
import { GPUPrimitiveTopology, GPUIndexFormat, GPUCompareFunction, GPUFrontFace, GPUCullMode, GPUVertexFormat, GPUBlendFactor, GPUBlendOperation, BlendColorFactor, OneMinusBlendColorFactor, GPUColorWriteFlags, GPUStencilOperation, GPUInputStepMode } from './constants.js'; |
|
import { |
|
FrontSide, BackSide, DoubleSide, |
|
NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth, |
|
NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc, |
|
KeepStencilOp, ZeroStencilOp, ReplaceStencilOp, InvertStencilOp, IncrementStencilOp, DecrementStencilOp, IncrementWrapStencilOp, DecrementWrapStencilOp, |
|
NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending, |
|
AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation, |
|
ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor |
|
} from 'three'; |
|
|
|
class WebGPURenderPipeline { |
|
|
|
constructor( device, renderer, sampleCount ) { |
|
|
|
this.cacheKey = null; |
|
this.shaderAttributes = null; |
|
this.stageVertex = null; |
|
this.stageFragment = null; |
|
this.usedTimes = 0; |
|
|
|
this._device = device; |
|
this._renderer = renderer; |
|
this._sampleCount = sampleCount; |
|
|
|
} |
|
|
|
init( cacheKey, stageVertex, stageFragment, object, nodeBuilder ) { |
|
|
|
const material = object.material; |
|
const geometry = object.geometry; |
|
|
|
// determine shader attributes |
|
|
|
const shaderAttributes = this._getShaderAttributes( nodeBuilder, geometry ); |
|
|
|
// vertex buffers |
|
|
|
const vertexBuffers = []; |
|
|
|
for ( const attribute of shaderAttributes ) { |
|
|
|
const name = attribute.name; |
|
const geometryAttribute = geometry.getAttribute( name ); |
|
const stepMode = ( geometryAttribute !== undefined && geometryAttribute.isInstancedBufferAttribute ) ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex; |
|
|
|
vertexBuffers.push( { |
|
arrayStride: attribute.arrayStride, |
|
attributes: [ { shaderLocation: attribute.slot, offset: 0, format: attribute.format } ], |
|
stepMode: stepMode |
|
} ); |
|
|
|
} |
|
|
|
this.cacheKey = cacheKey; |
|
this.shaderAttributes = shaderAttributes; |
|
this.stageVertex = stageVertex; |
|
this.stageFragment = stageFragment; |
|
|
|
// blending |
|
|
|
let alphaBlend = {}; |
|
let colorBlend = {}; |
|
|
|
if ( material.transparent === true && material.blending !== NoBlending ) { |
|
|
|
alphaBlend = this._getAlphaBlend( material ); |
|
colorBlend = this._getColorBlend( material ); |
|
|
|
} |
|
|
|
// stencil |
|
|
|
let stencilFront = {}; |
|
|
|
if ( material.stencilWrite === true ) { |
|
|
|
stencilFront = { |
|
compare: this._getStencilCompare( material ), |
|
failOp: this._getStencilOperation( material.stencilFail ), |
|
depthFailOp: this._getStencilOperation( material.stencilZFail ), |
|
passOp: this._getStencilOperation( material.stencilZPass ) |
|
}; |
|
|
|
} |
|
|
|
// |
|
|
|
const primitiveState = this._getPrimitiveState( object, material ); |
|
const colorWriteMask = this._getColorWriteMask( material ); |
|
const depthCompare = this._getDepthCompare( material ); |
|
const colorFormat = this._renderer.getCurrentColorFormat(); |
|
const depthStencilFormat = this._renderer.getCurrentDepthStencilFormat(); |
|
|
|
this.pipeline = this._device.createRenderPipeline( { |
|
vertex: Object.assign( {}, stageVertex.stage, { buffers: vertexBuffers } ), |
|
fragment: Object.assign( {}, stageFragment.stage, { targets: [ { |
|
format: colorFormat, |
|
blend: { |
|
alpha: alphaBlend, |
|
color: colorBlend |
|
}, |
|
writeMask: colorWriteMask |
|
} ] } ), |
|
primitive: primitiveState, |
|
depthStencil: { |
|
format: depthStencilFormat, |
|
depthWriteEnabled: material.depthWrite, |
|
depthCompare: depthCompare, |
|
stencilFront: stencilFront, |
|
stencilBack: {}, // three.js does not provide an API to configure the back function (gl.stencilFuncSeparate() was never used) |
|
stencilReadMask: material.stencilFuncMask, |
|
stencilWriteMask: material.stencilWriteMask |
|
}, |
|
multisample: { |
|
count: this._sampleCount |
|
} |
|
} ); |
|
|
|
} |
|
|
|
_getArrayStride( type, bytesPerElement ) { |
|
|
|
// @TODO: This code is GLSL specific. We need to update when we switch to WGSL. |
|
|
|
if ( type === 'float' || type === 'int' || type === 'uint' ) return bytesPerElement; |
|
if ( type === 'vec2' || type === 'ivec2' || type === 'uvec2' ) return bytesPerElement * 2; |
|
if ( type === 'vec3' || type === 'ivec3' || type === 'uvec3' ) return bytesPerElement * 3; |
|
if ( type === 'vec4' || type === 'ivec4' || type === 'uvec4' ) return bytesPerElement * 4; |
|
|
|
console.error( 'THREE.WebGPURenderer: Shader variable type not supported yet.', type ); |
|
|
|
} |
|
|
|
_getAlphaBlend( material ) { |
|
|
|
const blending = material.blending; |
|
const premultipliedAlpha = material.premultipliedAlpha; |
|
|
|
let alphaBlend = undefined; |
|
|
|
switch ( blending ) { |
|
|
|
case NormalBlending: |
|
|
|
if ( premultipliedAlpha === false ) { |
|
|
|
alphaBlend = { |
|
srcFactor: GPUBlendFactor.One, |
|
dstFactor: GPUBlendFactor.OneMinusSrcAlpha, |
|
operation: GPUBlendOperation.Add |
|
}; |
|
|
|
} |
|
|
|
break; |
|
|
|
case AdditiveBlending: |
|
// no alphaBlend settings |
|
break; |
|
|
|
case SubtractiveBlending: |
|
|
|
if ( premultipliedAlpha === true ) { |
|
|
|
alphaBlend = { |
|
srcFactor: GPUBlendFactor.OneMinusSrcColor, |
|
dstFactor: GPUBlendFactor.OneMinusSrcAlpha, |
|
operation: GPUBlendOperation.Add |
|
}; |
|
|
|
} |
|
|
|
break; |
|
|
|
case MultiplyBlending: |
|
if ( premultipliedAlpha === true ) { |
|
|
|
alphaBlend = { |
|
srcFactor: GPUBlendFactor.Zero, |
|
dstFactor: GPUBlendFactor.SrcAlpha, |
|
operation: GPUBlendOperation.Add |
|
}; |
|
|
|
} |
|
|
|
break; |
|
|
|
case CustomBlending: |
|
|
|
const blendSrcAlpha = material.blendSrcAlpha; |
|
const blendDstAlpha = material.blendDstAlpha; |
|
const blendEquationAlpha = material.blendEquationAlpha; |
|
|
|
if ( blendSrcAlpha !== null && blendDstAlpha !== null && blendEquationAlpha !== null ) { |
|
|
|
alphaBlend = { |
|
srcFactor: this._getBlendFactor( blendSrcAlpha ), |
|
dstFactor: this._getBlendFactor( blendDstAlpha ), |
|
operation: this._getBlendOperation( blendEquationAlpha ) |
|
}; |
|
|
|
} |
|
|
|
break; |
|
|
|
default: |
|
console.error( 'THREE.WebGPURenderer: Blending not supported.', blending ); |
|
|
|
} |
|
|
|
return alphaBlend; |
|
|
|
} |
|
|
|
_getBlendFactor( blend ) { |
|
|
|
let blendFactor; |
|
|
|
switch ( blend ) { |
|
|
|
case ZeroFactor: |
|
blendFactor = GPUBlendFactor.Zero; |
|
break; |
|
|
|
case OneFactor: |
|
blendFactor = GPUBlendFactor.One; |
|
break; |
|
|
|
case SrcColorFactor: |
|
blendFactor = GPUBlendFactor.SrcColor; |
|
break; |
|
|
|
case OneMinusSrcColorFactor: |
|
blendFactor = GPUBlendFactor.OneMinusSrcColor; |
|
break; |
|
|
|
case SrcAlphaFactor: |
|
blendFactor = GPUBlendFactor.SrcAlpha; |
|
break; |
|
|
|
case OneMinusSrcAlphaFactor: |
|
blendFactor = GPUBlendFactor.OneMinusSrcAlpha; |
|
break; |
|
|
|
case DstColorFactor: |
|
blendFactor = GPUBlendFactor.DstColor; |
|
break; |
|
|
|
case OneMinusDstColorFactor: |
|
blendFactor = GPUBlendFactor.OneMinusDstColor; |
|
break; |
|
|
|
case DstAlphaFactor: |
|
blendFactor = GPUBlendFactor.DstAlpha; |
|
break; |
|
|
|
case OneMinusDstAlphaFactor: |
|
blendFactor = GPUBlendFactor.OneMinusDstAlpha; |
|
break; |
|
|
|
case SrcAlphaSaturateFactor: |
|
blendFactor = GPUBlendFactor.SrcAlphaSaturated; |
|
break; |
|
|
|
case BlendColorFactor: |
|
blendFactor = GPUBlendFactor.BlendColor; |
|
break; |
|
|
|
case OneMinusBlendColorFactor: |
|
blendFactor = GPUBlendFactor.OneMinusBlendColor; |
|
break; |
|
|
|
|
|
default: |
|
console.error( 'THREE.WebGPURenderer: Blend factor not supported.', blend ); |
|
|
|
} |
|
|
|
return blendFactor; |
|
|
|
} |
|
|
|
_getBlendOperation( blendEquation ) { |
|
|
|
let blendOperation; |
|
|
|
switch ( blendEquation ) { |
|
|
|
case AddEquation: |
|
blendOperation = GPUBlendOperation.Add; |
|
break; |
|
|
|
case SubtractEquation: |
|
blendOperation = GPUBlendOperation.Subtract; |
|
break; |
|
|
|
case ReverseSubtractEquation: |
|
blendOperation = GPUBlendOperation.ReverseSubtract; |
|
break; |
|
|
|
case MinEquation: |
|
blendOperation = GPUBlendOperation.Min; |
|
break; |
|
|
|
case MaxEquation: |
|
blendOperation = GPUBlendOperation.Max; |
|
break; |
|
|
|
default: |
|
console.error( 'THREE.WebGPURenderer: Blend equation not supported.', blendEquation ); |
|
|
|
} |
|
|
|
return blendOperation; |
|
|
|
} |
|
|
|
_getColorBlend( material ) { |
|
|
|
const blending = material.blending; |
|
const premultipliedAlpha = material.premultipliedAlpha; |
|
|
|
const colorBlend = { |
|
srcFactor: null, |
|
dstFactor: null, |
|
operation: null |
|
}; |
|
|
|
switch ( blending ) { |
|
|
|
case NormalBlending: |
|
|
|
colorBlend.srcFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.One : GPUBlendFactor.SrcAlpha; |
|
colorBlend.dstFactor = GPUBlendFactor.OneMinusSrcAlpha; |
|
colorBlend.operation = GPUBlendOperation.Add; |
|
break; |
|
|
|
case AdditiveBlending: |
|
colorBlend.srcFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.One : GPUBlendFactor.SrcAlpha; |
|
colorBlend.operation = GPUBlendOperation.Add; |
|
break; |
|
|
|
case SubtractiveBlending: |
|
colorBlend.srcFactor = GPUBlendFactor.Zero; |
|
colorBlend.dstFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.Zero : GPUBlendFactor.OneMinusSrcColor; |
|
colorBlend.operation = GPUBlendOperation.Add; |
|
break; |
|
|
|
case MultiplyBlending: |
|
colorBlend.srcFactor = GPUBlendFactor.Zero; |
|
colorBlend.dstFactor = GPUBlendFactor.SrcColor; |
|
colorBlend.operation = GPUBlendOperation.Add; |
|
break; |
|
|
|
case CustomBlending: |
|
colorBlend.srcFactor = this._getBlendFactor( material.blendSrc ); |
|
colorBlend.dstFactor = this._getBlendFactor( material.blendDst ); |
|
colorBlend.operation = this._getBlendOperation( material.blendEquation ); |
|
break; |
|
|
|
default: |
|
console.error( 'THREE.WebGPURenderer: Blending not supported.', blending ); |
|
|
|
} |
|
|
|
return colorBlend; |
|
|
|
} |
|
|
|
_getColorWriteMask( material ) { |
|
|
|
return ( material.colorWrite === true ) ? GPUColorWriteFlags.All : GPUColorWriteFlags.None; |
|
|
|
} |
|
|
|
_getDepthCompare( material ) { |
|
|
|
let depthCompare; |
|
|
|
if ( material.depthTest === false ) { |
|
|
|
depthCompare = GPUCompareFunction.Always; |
|
|
|
} else { |
|
|
|
const depthFunc = material.depthFunc; |
|
|
|
switch ( depthFunc ) { |
|
|
|
case NeverDepth: |
|
depthCompare = GPUCompareFunction.Never; |
|
break; |
|
|
|
case AlwaysDepth: |
|
depthCompare = GPUCompareFunction.Always; |
|
break; |
|
|
|
case LessDepth: |
|
depthCompare = GPUCompareFunction.Less; |
|
break; |
|
|
|
case LessEqualDepth: |
|
depthCompare = GPUCompareFunction.LessEqual; |
|
break; |
|
|
|
case EqualDepth: |
|
depthCompare = GPUCompareFunction.Equal; |
|
break; |
|
|
|
case GreaterEqualDepth: |
|
depthCompare = GPUCompareFunction.GreaterEqual; |
|
break; |
|
|
|
case GreaterDepth: |
|
depthCompare = GPUCompareFunction.Greater; |
|
break; |
|
|
|
case NotEqualDepth: |
|
depthCompare = GPUCompareFunction.NotEqual; |
|
break; |
|
|
|
default: |
|
console.error( 'THREE.WebGPURenderer: Invalid depth function.', depthFunc ); |
|
|
|
} |
|
|
|
} |
|
|
|
return depthCompare; |
|
|
|
} |
|
|
|
_getPrimitiveState( object, material ) { |
|
|
|
const descriptor = {}; |
|
|
|
descriptor.topology = this._getPrimitiveTopology( object ); |
|
|
|
if ( object.isLine === true && object.isLineSegments !== true ) { |
|
|
|
const geometry = object.geometry; |
|
const count = ( geometry.index ) ? geometry.index.count : geometry.attributes.position.count; |
|
descriptor.stripIndexFormat = ( count > 65535 ) ? GPUIndexFormat.Uint32 : GPUIndexFormat.Uint16; // define data type for primitive restart value |
|
|
|
} |
|
|
|
switch ( material.side ) { |
|
|
|
case FrontSide: |
|
descriptor.frontFace = GPUFrontFace.CCW; |
|
descriptor.cullMode = GPUCullMode.Back; |
|
break; |
|
|
|
case BackSide: |
|
descriptor.frontFace = GPUFrontFace.CW; |
|
descriptor.cullMode = GPUCullMode.Back; |
|
break; |
|
|
|
case DoubleSide: |
|
descriptor.frontFace = GPUFrontFace.CCW; |
|
descriptor.cullMode = GPUCullMode.None; |
|
break; |
|
|
|
default: |
|
console.error( 'THREE.WebGPURenderer: Unknown Material.side value.', material.side ); |
|
break; |
|
|
|
} |
|
|
|
return descriptor; |
|
|
|
} |
|
|
|
_getPrimitiveTopology( object ) { |
|
|
|
if ( object.isMesh ) return GPUPrimitiveTopology.TriangleList; |
|
else if ( object.isPoints ) return GPUPrimitiveTopology.PointList; |
|
else if ( object.isLineSegments ) return GPUPrimitiveTopology.LineList; |
|
else if ( object.isLine ) return GPUPrimitiveTopology.LineStrip; |
|
|
|
} |
|
|
|
_getStencilCompare( material ) { |
|
|
|
let stencilCompare; |
|
|
|
const stencilFunc = material.stencilFunc; |
|
|
|
switch ( stencilFunc ) { |
|
|
|
case NeverStencilFunc: |
|
stencilCompare = GPUCompareFunction.Never; |
|
break; |
|
|
|
case AlwaysStencilFunc: |
|
stencilCompare = GPUCompareFunction.Always; |
|
break; |
|
|
|
case LessStencilFunc: |
|
stencilCompare = GPUCompareFunction.Less; |
|
break; |
|
|
|
case LessEqualStencilFunc: |
|
stencilCompare = GPUCompareFunction.LessEqual; |
|
break; |
|
|
|
case EqualStencilFunc: |
|
stencilCompare = GPUCompareFunction.Equal; |
|
break; |
|
|
|
case GreaterEqualStencilFunc: |
|
stencilCompare = GPUCompareFunction.GreaterEqual; |
|
break; |
|
|
|
case GreaterStencilFunc: |
|
stencilCompare = GPUCompareFunction.Greater; |
|
break; |
|
|
|
case NotEqualStencilFunc: |
|
stencilCompare = GPUCompareFunction.NotEqual; |
|
break; |
|
|
|
default: |
|
console.error( 'THREE.WebGPURenderer: Invalid stencil function.', stencilFunc ); |
|
|
|
} |
|
|
|
return stencilCompare; |
|
|
|
} |
|
|
|
_getStencilOperation( op ) { |
|
|
|
let stencilOperation; |
|
|
|
switch ( op ) { |
|
|
|
case KeepStencilOp: |
|
stencilOperation = GPUStencilOperation.Keep; |
|
break; |
|
|
|
case ZeroStencilOp: |
|
stencilOperation = GPUStencilOperation.Zero; |
|
break; |
|
|
|
case ReplaceStencilOp: |
|
stencilOperation = GPUStencilOperation.Replace; |
|
break; |
|
|
|
case InvertStencilOp: |
|
stencilOperation = GPUStencilOperation.Invert; |
|
break; |
|
|
|
case IncrementStencilOp: |
|
stencilOperation = GPUStencilOperation.IncrementClamp; |
|
break; |
|
|
|
case DecrementStencilOp: |
|
stencilOperation = GPUStencilOperation.DecrementClamp; |
|
break; |
|
|
|
case IncrementWrapStencilOp: |
|
stencilOperation = GPUStencilOperation.IncrementWrap; |
|
break; |
|
|
|
case DecrementWrapStencilOp: |
|
stencilOperation = GPUStencilOperation.DecrementWrap; |
|
break; |
|
|
|
default: |
|
console.error( 'THREE.WebGPURenderer: Invalid stencil operation.', stencilOperation ); |
|
|
|
} |
|
|
|
return stencilOperation; |
|
|
|
} |
|
|
|
_getVertexFormat( type, bytesPerElement ) { |
|
|
|
// float |
|
|
|
if ( type === 'float' ) return GPUVertexFormat.Float32; |
|
|
|
if ( type === 'vec2' ) { |
|
|
|
if ( bytesPerElement === 2 ) { |
|
|
|
return GPUVertexFormat.Float16x2; |
|
|
|
} else { |
|
|
|
return GPUVertexFormat.Float32x2; |
|
|
|
} |
|
|
|
} |
|
|
|
if ( type === 'vec3' ) return GPUVertexFormat.Float32x3; |
|
|
|
if ( type === 'vec4' ) { |
|
|
|
if ( bytesPerElement === 2 ) { |
|
|
|
return GPUVertexFormat.Float16x4; |
|
|
|
} else { |
|
|
|
return GPUVertexFormat.Float32x4; |
|
|
|
} |
|
|
|
} |
|
|
|
// int |
|
|
|
if ( type === 'int' ) return GPUVertexFormat.Sint32; |
|
|
|
if ( type === 'ivec2' ) { |
|
|
|
if ( bytesPerElement === 1 ) { |
|
|
|
return GPUVertexFormat.Sint8x2; |
|
|
|
} else if ( bytesPerElement === 2 ) { |
|
|
|
return GPUVertexFormat.Sint16x2; |
|
|
|
} else { |
|
|
|
return GPUVertexFormat.Sint32x2; |
|
|
|
} |
|
|
|
} |
|
|
|
if ( type === 'ivec3' ) return GPUVertexFormat.Sint32x3; |
|
|
|
if ( type === 'ivec4' ) { |
|
|
|
if ( bytesPerElement === 1 ) { |
|
|
|
return GPUVertexFormat.Sint8x4; |
|
|
|
} else if ( bytesPerElement === 2 ) { |
|
|
|
return GPUVertexFormat.Sint16x4; |
|
|
|
} else { |
|
|
|
return GPUVertexFormat.Sint32x4; |
|
|
|
} |
|
|
|
} |
|
|
|
// uint |
|
|
|
if ( type === 'uint' ) return GPUVertexFormat.Uint32; |
|
|
|
if ( type === 'uvec2' ) { |
|
|
|
if ( bytesPerElement === 1 ) { |
|
|
|
return GPUVertexFormat.Uint8x2; |
|
|
|
} else if ( bytesPerElement === 2 ) { |
|
|
|
return GPUVertexFormat.Uint16x2; |
|
|
|
} else { |
|
|
|
return GPUVertexFormat.Uint32x2; |
|
|
|
} |
|
|
|
} |
|
|
|
if ( type === 'uvec3' ) return GPUVertexFormat.Uint32x3; |
|
|
|
if ( type === 'uvec4' ) { |
|
|
|
if ( bytesPerElement === 1 ) { |
|
|
|
return GPUVertexFormat.Uint8x4; |
|
|
|
} else if ( bytesPerElement === 2 ) { |
|
|
|
return GPUVertexFormat.Uint16x4; |
|
|
|
} else { |
|
|
|
return GPUVertexFormat.Uint32x4; |
|
|
|
} |
|
|
|
} |
|
|
|
console.error( 'THREE.WebGPURenderer: Shader variable type not supported yet.', type ); |
|
|
|
} |
|
|
|
_getShaderAttributes( nodeBuilder, geometry ) { |
|
|
|
const nodeAttributes = nodeBuilder.attributes; |
|
const attributes = []; |
|
|
|
for ( let slot = 0; slot < nodeAttributes.length; slot ++ ) { |
|
|
|
const nodeAttribute = nodeAttributes[ slot ]; |
|
|
|
const name = nodeAttribute.name; |
|
const type = nodeAttribute.type; |
|
|
|
const geometryAttribute = geometry.getAttribute( name ); |
|
const bytesPerElement = ( geometryAttribute !== undefined ) ? geometryAttribute.array.BYTES_PER_ELEMENT : 4; |
|
|
|
const arrayStride = this._getArrayStride( type, bytesPerElement ); |
|
const format = this._getVertexFormat( type, bytesPerElement ); |
|
|
|
attributes.push( { |
|
name, |
|
arrayStride, |
|
format, |
|
slot |
|
} ); |
|
|
|
} |
|
|
|
return attributes; |
|
|
|
} |
|
|
|
} |
|
|
|
export default WebGPURenderPipeline;
|
|
|