public virtual void Render(SSRenderConfig renderConfig) { // compute and set the modelView matrix, by combining the cameraViewMat // with the object's world matrix // ... http://www.songho.ca/opengl/gl_transform.html // ... http://stackoverflow.com/questions/5798226/3d-graphics-processing-how-to-calculate-modelview-matrix renderBoundingSphereMesh(renderConfig); Matrix4 modelViewMat = this.worldMat * renderConfig.invCameraViewMatrix; if (this.renderState.matchScaleToScreenPixels) { Matrix4 scaleCompenstaion = OpenTKHelper.ScaleToScreenPxViewMat( this.Pos, this.Scale.X, ref renderConfig.invCameraViewMatrix, ref renderConfig.projectionMatrix); modelViewMat = scaleCompenstaion * modelViewMat; } if (this.renderState.doBillboarding) { modelViewMat = OpenTKHelper.BillboardMatrix(ref modelViewMat); } GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref modelViewMat); resetTexturingState(); if (renderConfig.drawingShadowMap) { if (renderConfig.drawingPssm && renderConfig.pssmShader != null) { renderConfig.pssmShader.Activate(); renderConfig.pssmShader.UniObjectWorldTransform = this.worldMat; } else { SSShaderProgram.DeactivateAll(); } } else { if (renderState.noShader) { SSShaderProgram.DeactivateAll(); } else { setDefaultShaderState(renderConfig.mainShader, renderConfig); } setMaterialState(); if (this.alphaBlendingEnabled) { GL.Enable(EnableCap.Blend); GL.BlendEquationSeparate(renderState.blendEquationModeRGB, renderState.blendEquationModeAlpha); GL.BlendFuncSeparate(renderState.blendFactorSrcRGB, renderState.blendFactorDestRGB, renderState.blendFactorSrcAlpha, renderState.blendFactorDestAlpha); //GL.B } else { GL.Disable(EnableCap.Blend); } if (this.renderState.lighted) { GL.Enable(EnableCap.Lighting); GL.ShadeModel(ShadingModel.Flat); } else { GL.Disable(EnableCap.Lighting); } } if (this.renderState.alphaTest) { GL.Enable(EnableCap.AlphaTest); } else { GL.Disable(EnableCap.AlphaTest); } if (this.renderState.depthTest) { GL.Enable(EnableCap.DepthTest); GL.DepthFunc(renderState.depthFunc); } else { GL.Disable(EnableCap.DepthTest); } GL.DepthMask(this.renderState.depthWrite); GL.Disable(EnableCap.LineStipple); if (preRenderHook != null) { preRenderHook(this, renderConfig); } }
protected void ComputeProjections( List <SSObject> objects, Matrix4 cameraView, Matrix4 cameraProj, float fov, float aspect, float cameraNearZ, float cameraFarZ) { if (m_light.GetType() != typeof(SSDirectionalLight)) { throw new NotSupportedException(); } SSDirectionalLight dirLight = (SSDirectionalLight)m_light; // light-aligned unit vectors Vector3 lightZ = dirLight.Direction.Normalized(); Vector3 lightX, lightY; OpenTKHelper.TwoPerpAxes(lightZ, out lightX, out lightY); // transform matrix from regular space into light aligned space Matrix4 lightTransform = new Matrix4( lightX.X, lightX.Y, lightX.Z, 0f, lightY.X, lightY.Y, lightY.Z, 0f, lightZ.X, lightZ.Y, lightZ.Z, 0f, 0f, 0f, 0f, 0f ); // Step 0: camera projection matrix (nearZ and farZ modified) for each frustum split float prevFarZ = cameraNearZ; for (int i = 0; i < c_numberOfSplits; ++i) { // generate frustum splits using Practical Split Scheme (GPU Gems 3, 10.2.1) float iRatio = (float)(i + 1) / (float)c_numberOfSplits; float cLog = cameraNearZ * (float)Math.Pow(cameraFarZ / cameraNearZ, iRatio); float cUni = cameraNearZ + (cameraFarZ - cameraNearZ) * iRatio; float nextFarZ = LogVsLinearSplitFactor * cLog + (1f - LogVsLinearSplitFactor) * cUni; float nextNearZ = prevFarZ; // exported to the shader m_viewSplits [i] = nextFarZ; // create a view proj matrix with the nearZ, farZ values for the current split m_frustumViewProjMatrices[i] = cameraView * Matrix4.CreatePerspectiveFieldOfView(fov, aspect, nextNearZ, nextFarZ); // create light-aligned AABBs of frustums m_frustumLightBB [i] = SSAABB.FromFrustum(ref lightTransform, ref m_frustumViewProjMatrices [i]); prevFarZ = nextFarZ; } #if true // Optional scene-dependent optimization for (int i = 0; i < c_numberOfSplits; ++i) { m_objsLightBB[i] = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); m_splitFrustums[i] = new SSFrustumCuller(ref m_frustumViewProjMatrices[i]); m_shrink[i] = false; } foreach (var obj in objects) { // pass through all shadow casters and receivers if (obj.renderState.toBeDeleted || obj.localBoundingSphereRadius <= 0f || !obj.renderState.visible || !obj.renderState.receivesShadows) { continue; } else { for (int i = 0; i < c_numberOfSplits; ++i) { if (m_splitFrustums[i].isSphereInsideFrustum(obj.worldBoundingSphere)) { // determine AABB in light coordinates of the objects so far m_shrink[i] = true; Vector3 lightAlignedPos = Vector3.Transform(obj.worldBoundingSphereCenter, lightTransform); Vector3 rad = new Vector3(obj.worldBoundingSphereRadius); Vector3 localMin = lightAlignedPos - rad; Vector3 localMax = lightAlignedPos + rad; m_objsLightBB[i].UpdateMin(localMin); m_objsLightBB[i].UpdateMax(localMax); } } } } #endif for (int i = 0; i < c_numberOfSplits; ++i) { if (m_shrink [i]) { m_resultLightBB[i].Min = Vector3.ComponentMax(m_frustumLightBB [i].Min, m_objsLightBB [i].Min); m_resultLightBB [i].Max = Vector3.ComponentMin(m_frustumLightBB [i].Max, m_objsLightBB [i].Max); } else { m_resultLightBB [i] = m_frustumLightBB [i]; } } for (int i = 0; i < c_numberOfSplits; ++i) { // Obtain view + projection + crop matrix, need it later Matrix4 shadowView, shadowProj; viewProjFromLightAlignedBB(ref m_resultLightBB [i], ref lightTransform, ref lightY, out shadowView, out shadowProj); m_shadowViewProjMatrices[i] = shadowView * shadowProj * c_cropMatrices[i]; // obtain view + projection + clio + bias m_shadowViewProjBiasMatrices[i] = m_shadowViewProjMatrices[i] * c_biasMatrix; // There is, currently, no mathematically derived solution to how much Poisson spread scaling // you need for each split. Current improvisation combines 1) increasing spread for the near // splits; reducing spread for the far splits and 2) reducing spread for splits with larger // light-aligned areas; increasing spread for splits with smaller light-aligned areas m_poissonScaling [i] = m_resultLightBB [i].Diff().Xy / (100f * (float)Math.Pow(3.0, i - 1)); } // Combine all splits' BB into one and extend it to include shadow casters closer to light SSAABB castersLightBB = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); for (int i = 0; i < c_numberOfSplits; ++i) { castersLightBB.Combine(ref m_resultLightBB [i]); } // extend Z of the AABB to cover shadow-casters closer to the light foreach (var obj in objects) { if (obj.renderState.toBeDeleted || obj.localBoundingSphereRadius <= 0f || !obj.renderState.visible || !obj.renderState.castsShadow) { continue; } else { Vector3 lightAlignedPos = Vector3.Transform(obj.worldBoundingSphereCenter, lightTransform); Vector3 rad = new Vector3(obj.worldBoundingSphereRadius); Vector3 localMin = lightAlignedPos - rad; Vector3 localMax = lightAlignedPos + rad; if (localMin.Z < castersLightBB.Min.Z) { if (OpenTKHelper.RectsOverlap(castersLightBB.Min.Xy, castersLightBB.Max.Xy, localMin.Xy, localMax.Xy)) { castersLightBB.Min.Z = localMin.Z; } } } } // Generate frustum culler from the BB extended towards light to include shadow casters Matrix4 frustumView, frustumProj; viewProjFromLightAlignedBB(ref castersLightBB, ref lightTransform, ref lightY, out frustumView, out frustumProj); Matrix4 frustumMatrix = frustumView * frustumProj; FrustumCuller = new SSFrustumCuller(ref frustumMatrix); }
public override void sortByDepth(ref Matrix4 viewMatrix) { if (_numParticles == 0) { return; } int numAlive = 0; float prevViewDepth = float.NegativeInfinity; bool alreadySorted = true; for (int i = 0; i < _activeBlockLength; ++i) { if (isAlive(i)) { // Do the transform and store z of the result Vector3 pos = _readElement(_positions, i).Value; pos = Vector3.Transform(pos, viewMatrix); float viewDepth = pos.Z; writeDataIfNeeded(ref _viewDepths, i, viewDepth); ++numAlive; if (alreadySorted && pos.Z < prevViewDepth) { alreadySorted = false; } prevViewDepth = viewDepth; } else { // since we are doing a sort pass later, might as well make it so // so the dead particles get pushed the back of the arrays writeDataIfNeeded(ref _viewDepths, i, float.PositiveInfinity); alreadySorted = false; //force a "sort" to puch dead particles towards the end } } if (!alreadySorted) { quickSort(0, _activeBlockLength - 1); // somewhat hacky workaround for zombie particles that seem to be related to sortByDepth() _numParticles = numAlive; _activeBlockLength = _numParticles; if (_numParticles == 0) { _nextIdxToWrite = _nextIdxToOverwrite = 0; } else if (_numParticles < _capacity) { // update pointers to reflect dead particles that just got sorted to the back _nextIdxToWrite = _numParticles - 1; } #if DEBUG_PARTICLE_SORTING ++debugNumSorted; #endif } else { #if DEBUG_PARTICLE_SORTING ++debugNumSortSkipped; #endif } #if DEBUG_PARTICLE_SORTING System.Console.WriteLine( "particle data actually sorted " + ((float)debugNumSorted / (float)(debugNumSorted + debugNumSortSkipped) * 100f) + "% of the time"); #endif #if false // set color based on the index of the particle in the arrays SSAttributeColor[] debugColors = { new SSAttributeColor(OpenTKHelper.Color4toRgba(Color4.Red)), new SSAttributeColor(OpenTKHelper.Color4toRgba(Color4.Green)), new SSAttributeColor(OpenTKHelper.Color4toRgba(Color4.Blue)), new SSAttributeColor(OpenTKHelper.Color4toRgba(Color4.Yellow)) }; for (int i = 0; i < m_activeBlockLength; ++i) { writeDataIfNeeded(ref m_colors, i, debugColors[i]); } #endif }
public SSShadowMapBase(TextureUnit texUnit, int textureWidth, int textureHeight) { if (!OpenTKHelper.areFramebuffersSupported()) { m_isValid = false; return; } #if false if (s_numberOfShadowMaps >= c_maxNumberOfShadowMaps) { throw new Exception("Unsupported number of shadow maps: " + (c_maxNumberOfShadowMaps + 1)); } #endif ++s_numberOfShadowMaps; m_frameBufferID = GL.Ext.GenFramebuffer(); if (m_frameBufferID < 0) { throw new Exception("gen fb failed"); } m_textureID = GL.GenTexture(); m_textureWidth = textureWidth; m_textureHeight = textureHeight; // bind the texture and set it up... m_textureUnit = texUnit; BindShadowMapToTexture(); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.DepthComponent32f, m_textureWidth, m_textureHeight, 0, PixelFormat.DepthComponent, PixelType.Float, IntPtr.Zero); // done creating texture, unbind GL.BindTexture(TextureTarget.Texture2D, 0); // ---------------------------- // now bind the texture to framebuffer.. GL.Ext.BindFramebuffer(FramebufferTarget.DrawFramebuffer, m_frameBufferID); GL.Ext.BindFramebuffer(FramebufferTarget.ReadFramebuffer, m_frameBufferID); GL.Ext.FramebufferTexture2D(FramebufferTarget.DrawFramebuffer, FramebufferAttachment.DepthAttachment, TextureTarget.Texture2D, m_textureID, 0); //GL.Ext.FramebufferTexture (FramebufferTarget.FramebufferExt, FramebufferAttachment.Color, //(int)All.None, 0); GL.Viewport(0, 0, m_textureWidth, m_textureHeight); GL.DrawBuffer(DrawBufferMode.None); GL.ReadBuffer(ReadBufferMode.None); if (!assertFramebufferOK(FramebufferTarget.DrawFramebuffer)) { throw new Exception("failed to create-and-bind shadowmap FBO"); } // leave in a sane state... unbindFramebuffer(); GL.ActiveTexture(TextureUnit.Texture0); if (!assertFramebufferOK(FramebufferTarget.DrawFramebuffer)) { throw new Exception("failed to ubind shadowmap FBO"); } }
private void ComputeProjections(List <SSObject> objects, SSLightBase light, Matrix4 cameraView, Matrix4 cameraProj) { if (light.GetType() != typeof(SSDirectionalLight)) { throw new NotSupportedException(); } SSDirectionalLight dirLight = (SSDirectionalLight)light; // light-aligned unit vectors Vector3 lightZ = dirLight.Direction.Normalized(); Vector3 lightX, lightY; OpenTKHelper.TwoPerpAxes(lightZ, out lightX, out lightY); // transform matrix from regular space into light aligned space Matrix4 lightTransform = new Matrix4( lightX.X, lightX.Y, lightX.Z, 0f, lightY.X, lightY.Y, lightY.Z, 0f, lightZ.X, lightZ.Y, lightZ.Z, 0f, 0f, 0f, 0f, 0f ); // Find AABB of frustum corners in light coordinates Matrix4 cameraViewProj = cameraView * cameraProj; SSAABB frustumLightBB = SSAABB.FromFrustum(ref lightTransform, ref cameraViewProj); bool shrink = false; SSAABB objsLightBB = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); #if true // (optional) scene dependent optimization // Trim the light-bounding box by the shadow receivers (only in light-space x,y,maxz) SSFrustumCuller cameraFrustum = new SSFrustumCuller(ref cameraViewProj); foreach (var obj in objects) { // pass through all shadow casters and receivers if (obj.renderState.toBeDeleted || !obj.renderState.visible || !obj.renderState.receivesShadows || obj.localBoundingSphereRadius <= 0f) { continue; } else if (cameraFrustum.isSphereInsideFrustum(obj.worldBoundingSphere)) { // determine AABB in light coordinates of the objects so far shrink = true; Vector3 lightAlignedPos = Vector3.Transform(obj.worldBoundingSphereCenter, lightTransform); Vector3 rad = new Vector3(obj.worldBoundingSphereRadius); Vector3 localMin = lightAlignedPos - rad; Vector3 localMax = lightAlignedPos + rad; objsLightBB.UpdateMin(localMin); objsLightBB.UpdateMax(localMax); } } #endif // Optimize the light-frustum-projection bounding box by the object-bounding-box SSAABB resultLightBB = new SSAABB(float.PositiveInfinity, float.NegativeInfinity); if (shrink) { // shrink the XY & far-Z coordinates.. resultLightBB.Min.Xy = Vector2.ComponentMax(frustumLightBB.Min.Xy, objsLightBB.Min.Xy); resultLightBB.Min.Z = objsLightBB.Min.Z; resultLightBB.Max = Vector3.ComponentMin(frustumLightBB.Max, objsLightBB.Max); } else { resultLightBB = frustumLightBB; } // View and projection matrices, used by the scene later viewProjFromLightAlignedBB(ref resultLightBB, ref lightTransform, ref lightY, out m_shadowViewMatrix, out m_shadowProjMatrix); // Now extend Z of the result AABB to cover shadow-casters closer to the light inside the // original box foreach (var obj in objects) { if (obj.renderState.toBeDeleted || !obj.renderState.visible || !obj.renderState.castsShadow || obj.localBoundingSphereRadius <= 0f) { continue; } Vector3 lightAlignedPos = Vector3.Transform(obj.worldBoundingSphereCenter, lightTransform); Vector3 rad = new Vector3(obj.worldBoundingSphereRadius); Vector3 localMin = lightAlignedPos - rad; if (localMin.Z < resultLightBB.Min.Z) { Vector3 localMax = lightAlignedPos + rad; if (OpenTKHelper.RectsOverlap(resultLightBB.Min.Xy, resultLightBB.Max.Xy, localMin.Xy, localMax.Xy)) { resultLightBB.Min.Z = localMin.Z; } } } // Generate frustum culler from the BB extended towards light to include shadow casters Matrix4 frustumView, frustumProj; viewProjFromLightAlignedBB(ref resultLightBB, ref lightTransform, ref lightY, out frustumView, out frustumProj); Matrix4 frustumMatrix = frustumView * frustumProj; FrustumCuller = new SSFrustumCuller(ref frustumMatrix); }
//--------------------- public override bool preciseIntersect(ref SSRay localRay, out float nearestLocalRayContact) { nearestLocalRayContact = float.PositiveInfinity; if (useBVHForIntersections) { if (_bvh == null && _vbo != null && _ibo != null) { // rebuilding BVH // TODO try updating instead of rebuilding? _bvh = new SSIndexedMeshTrianglesBVH(_vbo, _ibo); for (UInt16 triIdx = 0; triIdx < _ibo.numIndices / 3; ++triIdx) { _bvh.addObject(triIdx); } Console.WriteLine("New BVH MaxDepth = {0}", _bvh.maxDepth); } if (_bvh != null) { List <ssBVHNode <UInt16> > nodesHit = _bvh.traverseRay(localRay); foreach (var node in nodesHit) { if (!node.IsLeaf) { continue; } foreach (UInt16 triIdx in node.gobjects) { Vector3 v0, v1, v2; _readTriangleVertices(triIdx, out v0, out v1, out v2); float contact; if (OpenTKHelper.TriangleRayIntersectionTest( ref v0, ref v1, ref v2, ref localRay.pos, ref localRay.dir, out contact)) { if (contact < nearestLocalRayContact) { nearestLocalRayContact = contact; } } } } } } else { _bvh = null; // slow, tedious intersection test int numTri = lastAssignedIndices.Length / 3; for (UInt16 triIdx = 0; triIdx < numTri; ++triIdx) { Vector3 v0, v1, v2; _readTriangleVertices(triIdx, out v0, out v1, out v2); float contact; if (OpenTKHelper.TriangleRayIntersectionTest( ref v0, ref v1, ref v2, ref localRay.pos, ref localRay.dir, out contact)) { if (contact < nearestLocalRayContact) { nearestLocalRayContact = contact; } } } } return(nearestLocalRayContact < float.PositiveInfinity); }
//protected readonly STrailUpdater _updater; #endregion public STrailsData( PositionFunc positionFunc, FwdFunc fwdDirFunc, UpFunc upFunc, STrailsParameters trailsParams = null) : base(trailsParams.capacity) { this.trailsParams = trailsParams; this._positionFunc = positionFunc; this._fwdFunc = fwdDirFunc; this._upFunc = upFunc; _headSegmentIdxs = new ushort[trailsParams.numJets]; _tailSegmentIdxs = new ushort[trailsParams.numJets]; _prevSplineIntervalEndPos = new Vector3[trailsParams.numJets]; _prevSplineIntervalEndSlope = new Vector3[trailsParams.numJets]; _localJetOrients = new Matrix4[trailsParams.numJets]; Vector3 pos = _positionFunc(); Vector3 fwd = _fwdFunc(); Vector3 up = _upFunc(); Vector3 right = Vector3.Cross(fwd, up); Matrix4 globalOrient = new Matrix4( new Vector4(right, 0f), new Vector4(up, 0f), new Vector4(fwd, 0f), new Vector4(0f, 0f, 0f, 1f)); for (int i = 0; i < trailsParams.numJets; ++i) { _headSegmentIdxs[i] = STrailsSegment.NotConnected; _tailSegmentIdxs[i] = STrailsSegment.NotConnected; Vector3 localFwd = trailsParams.localJetDir(i); _localJetOrients[i] = OpenTKHelper.neededRotationMat(-Vector3.UnitZ, localFwd); jetTxfm(i, ref pos, ref globalOrient, out _prevSplineIntervalEndPos[i], out _prevSplineIntervalEndSlope[i]); } _outerColorEffector = new SSColorKeyframesEffector(trailsParams.outerColorKeyframes) { particleLifetime = trailsParams.trailLifetime, }; addEffector(_outerColorEffector); _innerColorEffector = new STrailsInnerColorEffector(trailsParams.innerColorKeyframes) { particleLifetime = trailsParams.trailLifetime, }; addEffector(_innerColorEffector); _innerRatioEffector = new STrailsInnerColorRatioEffector(trailsParams.innerColorRatioKeyframes) { particleLifetime = trailsParams.trailLifetime, }; addEffector(_innerRatioEffector); _outerRatioEffector = new STrailsOuterColorRatioEffector(trailsParams.outerColorRatioKeyframes) { particleLifetime = trailsParams.trailLifetime, }; addEffector(_outerRatioEffector); _widthEffector = new STrailsWidthEffector(trailsParams.widthKeyFrames) { particleLifetime = trailsParams.trailLifetime, }; addEffector(_widthEffector); //_updater = new STrailUpdater(trailsParams); //addEffector(_updater); }
public override void Render(SSRenderConfig renderConfig) { Matrix4 modelView = this.worldMat * renderConfig.invCameraViewMatrix; // allow particle system to react to camera/worldview particleSystem.updateCamera(ref modelView, ref renderConfig.projectionMatrix); // do we have anything to draw? if (particleSystem.activeBlockLength <= 0) { return; } base.Render(renderConfig); // select either instance shader or instance pssm shader ISSInstancableShaderProgram instanceShader = renderConfig.instanceShader; if (renderConfig.drawingShadowMap) { if (renderConfig.drawingPssm) { renderConfig.instancePssmShader.Activate(); renderConfig.instancePssmShader.UniObjectWorldTransform = this.worldMat; instanceShader = renderConfig.instancePssmShader; } } else { if (!globalBillboarding && base.alphaBlendingEnabled) { // Must be called before updating buffers particleSystem.sortByDepth(ref modelView); // Fixes flicker issues for particles with "fighting" view depth values // Also assumes the particle system is the last to be drawn in a scene GL.DepthFunc(DepthFunction.Lequal); } if (depthRead) { GL.Enable(EnableCap.DepthTest); } else { GL.Disable(EnableCap.DepthTest); } GL.DepthMask(depthWrite); // texture binding setup renderConfig.instanceShader.Activate(); renderConfig.instanceShader.UniObjectWorldTransform = this.worldMat; if (base.textureMaterial != null) { renderConfig.instanceShader.SetupTextures(base.textureMaterial); } } if (globalBillboarding) { // Setup "global" billboarding. (entire particle system is rendered as a camera-facing // billboard and will show the same position of particles from all angles) modelView = OpenTKHelper.BillboardMatrix(ref modelView); GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref modelView); } instanceShader.Activate(); // prepare attribute arrays for draw GL.PushClientAttrib(ClientAttribMask.ClientAllAttribBits); prepareAttribute(_posBuffer, instanceShader.AttrInstancePos, particleSystem.positions); prepareAttribute(_orientationXYBuffer, instanceShader.AttrInstanceOrientationXY, particleSystem.orientationsXY); prepareAttribute(_orientationZBuffer, instanceShader.AttrInstanceOrientationZ, particleSystem.orientationsZ); prepareAttribute(_masterScaleBuffer, instanceShader.AttrInstanceMasterScale, particleSystem.masterScales); prepareAttribute(_componentScaleXYBuffer, instanceShader.AttrInstanceComponentScaleXY, particleSystem.componentScalesXY); prepareAttribute(_componentScaleZBuffer, instanceShader.AttrInstanceComponentScaleZ, particleSystem.componentScalesZ); prepareAttribute(_colorBuffer, instanceShader.AttrInstanceColor, particleSystem.colors); //prepareAttribute(m_spriteIndexBuffer, instanceShader.AttrInstanceSpriteIndex, m_ps.SpriteIndices); prepareAttribute(_spriteOffsetUBuffer, instanceShader.AttrInstanceSpriteOffsetU, particleSystem.spriteOffsetsU); prepareAttribute(_spriteOffsetVBuffer, instanceShader.AttrInstanceSpriteOffsetV, particleSystem.SpriteOffsetsV); prepareAttribute(_spriteSizeUBuffer, instanceShader.AttrInstanceSpriteSizeU, particleSystem.SpriteSizesU); prepareAttribute(_spriteSizeVBuffer, instanceShader.AttrInstanceSpriteSizeV, particleSystem.SpriteSizesV); // do the draw mesh.renderInstanced(renderConfig, particleSystem.activeBlockLength, PrimitiveType.Triangles); GL.PopClientAttrib(); //this.boundingSphere.Render(ref renderConfig); }