@@ -50,7 +50,7 @@ public sealed class Simple3DRenderer : IDisposable
5050 /// <summary>
5151 /// Reference tracker managing engine-level compiled shader assets and texture definitions.
5252 /// </summary>
53- private readonly GlResourceManager _resources ;
53+ private readonly GlResourceManager ? _resources ;
5454
5555 /// <summary>
5656 /// Hardware layout buffer markers tracking active states
@@ -87,6 +87,12 @@ public sealed class Simple3DRenderer : IDisposable
8787 /// <summary>
8888 /// The active program
8989 /// </summary>
90+ /// <summary>
91+ /// Baked meshes that carry translucent geometry (glass, water) drawn this frame. They are drawn once the opaque
92+ /// world is complete, in <see cref="DrawTranslucentMeshes"/>, so they blend with what lies behind them.
93+ /// </summary>
94+ private readonly List < GpuMesh > _translucentMeshes = new ( ) ;
95+
9096 private int _activeProgram = - 1 ;
9197
9298 /// <summary>
@@ -220,7 +226,7 @@ public sealed class Simple3DRenderer : IDisposable
220226 /// <param name="width">The target canvas width.</param>
221227 /// <param name="height">The target canvas height.</param>
222228 /// <param name="resources">The shared unmanaged resource coordinator.</param>
223- public Simple3DRenderer ( int width , int height , GlResourceManager resources )
229+ public Simple3DRenderer ( int width , int height , GlResourceManager ? resources )
224230 {
225231 _resources = resources ;
226232 UpdateProjection ( width , height ) ;
@@ -539,6 +545,7 @@ public void BeginFrame()
539545 _activeProgram = - 1 ;
540546 _activeVao = - 1 ;
541547 _activeTexture = - 1 ;
548+ _translucentMeshes . Clear ( ) ;
542549
543550 ApplyFrameState ( ) ;
544551
@@ -590,6 +597,9 @@ public void BeginFrame()
590597 /// </summary>
591598 public void EndFrame ( )
592599 {
600+ // Everything opaque is in the depth buffer by now: blend the glass and the water over it.
601+ DrawTranslucentMeshes ( ) ;
602+
593603 if ( ! _framePostProcessed || ! _fboInitialized ) return ;
594604
595605 // 1. Restore GLWpfControl's target FBO and Viewport
@@ -808,6 +818,22 @@ public unsafe void Flush(RenderBatch batch)
808818 GL . DrawArrays ( PrimitiveType . Triangles , 0 , batch . Solid3DVertices . Length / 7 ) ;
809819 }
810820
821+ if ( batch . Translucent3DVertices . Length > 0 )
822+ {
823+ BindProgram ( _shaderSolid ) ;
824+ BindVao ( _vaoSolid ) ;
825+ EnsureSolidMatrices ( ) ;
826+
827+ EnsureBufferCapacity ( _vboSolid , ref _vboSolidCapacity , batch . Translucent3DVertices . Length ) ;
828+ GL . BindBuffer ( BufferTarget . ArrayBuffer , _vboSolid ) ;
829+ GL . BufferSubData ( BufferTarget . ArrayBuffer , IntPtr . Zero ,
830+ batch . Translucent3DVertices . Length * sizeof ( float ) , ( IntPtr ) batch . Translucent3DVertices . Pointer ) ;
831+
832+ BeginBlendedPass ( ) ;
833+ GL . DrawArrays ( PrimitiveType . Triangles , 0 , batch . Translucent3DVertices . Length / 7 ) ;
834+ EndBlendedPass ( ) ;
835+ }
836+
811837 if ( batch . Textured3DBatches . Count > 0 )
812838 {
813839 BindProgram ( _shaderTex ) ;
@@ -844,6 +870,53 @@ public unsafe void Flush(RenderBatch batch)
844870 BindVao ( 0 ) ;
845871 }
846872
873+ /// <summary>
874+ /// Switches to alpha blending without depth writes: translucent surfaces still hide behind opaque ones (depth
875+ /// test on) but do not hide each other or what is drawn after them.
876+ /// </summary>
877+ private static void BeginBlendedPass ( )
878+ {
879+ GL . Enable ( EnableCap . Blend ) ;
880+ GL . BlendFunc ( BlendingFactor . SrcAlpha , BlendingFactor . OneMinusSrcAlpha ) ;
881+ GL . DepthMask ( false ) ;
882+ }
883+
884+ /// <summary>
885+ /// Back to the opaque state set by <see cref="ApplyFrameState"/>.
886+ /// </summary>
887+ private static void EndBlendedPass ( )
888+ {
889+ GL . DepthMask ( true ) ;
890+ GL . Disable ( EnableCap . Blend ) ;
891+ }
892+
893+ /// <summary>
894+ /// Draws the translucent part (glass, water) of every mesh drawn this frame, after all opaque geometry.
895+ /// </summary>
896+ private void DrawTranslucentMeshes ( )
897+ {
898+ if ( _translucentMeshes . Count == 0 ) return ;
899+
900+ EnsureInitialized ( ) ;
901+
902+ BindProgram ( _shaderSolid ) ;
903+ EnsureSolidMatrices ( ) ;
904+ BeginBlendedPass ( ) ;
905+
906+ for ( var i = 0 ; i < _translucentMeshes . Count ; i ++ )
907+ {
908+ var mesh = _translucentMeshes [ i ] ;
909+ if ( mesh . TranslucentVao == 0 || mesh . TranslucentVertexCount <= 0 ) continue ;
910+
911+ BindVao ( mesh . TranslucentVao ) ;
912+ GL . DrawArrays ( PrimitiveType . Triangles , 0 , mesh . TranslucentVertexCount ) ;
913+ }
914+
915+ EndBlendedPass ( ) ;
916+ BindVao ( 0 ) ;
917+ _translucentMeshes . Clear ( ) ;
918+ }
919+
847920 /// <summary>
848921 /// Renders a persistent mesh directly from GPU VRAM with zero PCIe memory transfers.
849922 /// </summary>
@@ -861,6 +934,8 @@ public void DrawStaticMesh(GpuMesh mesh)
861934 GL . DrawArrays ( PrimitiveType . Triangles , 0 , mesh . SolidVertexCount ) ;
862935 }
863936
937+ if ( mesh . TranslucentVertexCount > 0 ) _translucentMeshes . Add ( mesh ) ;
938+
864939 if ( mesh . Ranges . Count == 0 ) return ;
865940
866941 BindProgram ( _shaderTex ) ;
0 commit comments