@@ -81,12 +81,15 @@ class WebGPUPipelineUtils {
8181 }
8282
8383 /**
84- * Creates a render pipeline for the given render object.
84+ * Builds a GPURenderPipelineDescriptor for the given render object.
8585 *
86+ * @private
8687 * @param {RenderObject } renderObject - The render object.
87- * @param {Array<Promise> } promises - An array of compilation promises which are used in `compileAsync()`.
88+ * @param {Object } [options={}] - Optional configuration.
89+ * @param {?boolean } [options.frontFaceCW=false] - Controls the primitive front-face orientation; defaults to CCW.
90+ * @return {Object } The render pipeline descriptor ready for createRenderPipeline.
8891 */
89- createRenderPipeline ( renderObject , promises ) {
92+ _buildRenderPipelineDescriptor ( renderObject , { frontFaceCW = false } = { } ) {
9093
9194 const { object, material, geometry, pipeline } = renderObject ;
9295 const { vertexProgram, fragmentProgram } = pipeline ;
@@ -95,7 +98,6 @@ class WebGPUPipelineUtils {
9598 const device = backend . device ;
9699 const utils = backend . utils ;
97100
98- const pipelineData = backend . get ( pipeline ) ;
99101
100102 // bind group layouts
101103
@@ -173,14 +175,15 @@ class WebGPUPipelineUtils {
173175 const vertexModule = backend . get ( vertexProgram ) . module ;
174176 const fragmentModule = backend . get ( fragmentProgram ) . module ;
175177
176- const primitiveState = this . _getPrimitiveState ( object , geometry , material ) ;
178+ const primitiveState = this . _getPrimitiveState ( object , geometry , material , frontFaceCW ) ;
179+
177180 const depthCompare = this . _getDepthCompare ( material ) ;
178181 const depthStencilFormat = utils . getCurrentDepthStencilFormat ( renderObject . context ) ;
179182
180183 const sampleCount = this . _getSampleCount ( renderObject . context ) ;
181184
182185 const pipelineDescriptor = {
183- label : `renderPipeline_${ material . name || material . type } _${ material . id } ` ,
186+ label : `renderPipeline_${ material . name || material . type } _${ material . id } ${ frontFaceCW ? '_CW' : '' } ` ,
184187 vertex : Object . assign ( { } , vertexModule , { buffers : vertexBuffers } ) ,
185188 fragment : Object . assign ( { } , fragmentModule , { targets } ) ,
186189 primitive : primitiveState ,
@@ -229,6 +232,26 @@ class WebGPUPipelineUtils {
229232
230233 }
231234
235+ return pipelineDescriptor ;
236+
237+ }
238+
239+ /**
240+ * Creates a render pipeline for the given render object.
241+ *
242+ * @param {RenderObject } renderObject - The render object.
243+ * @param {Array<Promise> } promises - An array of compilation promises which are used in `compileAsync()`.
244+ */
245+ createRenderPipeline ( renderObject , promises ) {
246+
247+ const { pipeline } = renderObject ;
248+
249+ const backend = this . backend ;
250+ const device = backend . device ;
251+
252+ const pipelineData = backend . get ( pipeline ) ;
253+
254+ const pipelineDescriptor = this . _buildRenderPipelineDescriptor ( renderObject ) ;
232255
233256 if ( promises === null ) {
234257
@@ -253,6 +276,54 @@ class WebGPUPipelineUtils {
253276
254277 }
255278
279+ /**
280+ * Returns a render pipeline variant with the requested front-face orientation.
281+ * If necessary, a new pipeline is created lazily and cached.
282+ *
283+ * @param {RenderObject } renderObject - The render object.
284+ * @param {boolean } frontFaceCW - Whether the front face should be CW (true) or CCW (false).
285+ * @return {GPURenderPipeline } The pipeline for the requested orientation.
286+ */
287+ getRenderPipelineVariant ( renderObject , frontFaceCW ) {
288+
289+ const { material, pipeline } = renderObject ;
290+
291+ const backend = this . backend ;
292+ const device = backend . device ;
293+
294+ const pipelineData = backend . get ( pipeline ) ;
295+
296+ console . assert ( pipelineData . pipeline !== undefined , 'Pipeline not created' ) ;
297+
298+ // DoubleSide does not cull, orientation is irrelevant
299+ if ( material . side === DoubleSide ) {
300+
301+ return pipelineData . pipeline ;
302+
303+ }
304+
305+ // Default CCW pipeline already handled by createRenderPipeline()
306+ if ( frontFaceCW === false ) {
307+
308+ return pipelineData . pipeline ;
309+
310+ }
311+
312+ // Need CW variant
313+ if ( pipelineData . pipelineCW !== undefined ) {
314+
315+ return pipelineData . pipelineCW ;
316+
317+ }
318+
319+ // Build a CW-oriented pipeline by reusing the shared descriptor with frontFace override
320+ const pipelineDescriptor = this . _buildRenderPipelineDescriptor ( renderObject , { frontFaceCW : true } ) ;
321+ pipelineData . pipelineCW = device . createRenderPipeline ( pipelineDescriptor ) ;
322+
323+ return pipelineData . pipelineCW ;
324+
325+ }
326+
256327 /**
257328 * Creates GPU render bundle encoder for the given render context.
258329 *
@@ -665,9 +736,10 @@ class WebGPUPipelineUtils {
665736 * @param {Object3D } object - The 3D object.
666737 * @param {BufferGeometry } geometry - The geometry.
667738 * @param {Material } material - The material.
739+ * @param {?boolean } [frontFaceCW=false] - Primitive front-face orientation; false=CCW (default), true=CW.
668740 * @return {Object } The primitive state.
669741 */
670- _getPrimitiveState ( object , geometry , material ) {
742+ _getPrimitiveState ( object , geometry , material , frontFaceCW = false ) {
671743
672744 const descriptor = { } ;
673745 const utils = this . backend . utils ;
@@ -683,17 +755,17 @@ class WebGPUPipelineUtils {
683755 switch ( material . side ) {
684756
685757 case FrontSide :
686- descriptor . frontFace = GPUFrontFace . CCW ;
758+ descriptor . frontFace = frontFaceCW ? GPUFrontFace . CW : GPUFrontFace . CCW ;
687759 descriptor . cullMode = GPUCullMode . Back ;
688760 break ;
689761
690762 case BackSide :
691- descriptor . frontFace = GPUFrontFace . CCW ;
763+ descriptor . frontFace = frontFaceCW ? GPUFrontFace . CW : GPUFrontFace . CCW ;
692764 descriptor . cullMode = GPUCullMode . Front ;
693765 break ;
694766
695767 case DoubleSide :
696- descriptor . frontFace = GPUFrontFace . CCW ;
768+ descriptor . frontFace = frontFaceCW ? GPUFrontFace . CW : GPUFrontFace . CCW ;
697769 descriptor . cullMode = GPUCullMode . None ;
698770 break ;
699771
0 commit comments