void finalGatherSingleThreaded(gatherWorkerData wd) { { float[] AOVals = TerrainGlobals.getEditor().getAmbientOcclusionValues(); Vector3[] normals = TerrainGlobals.getEditor().getNormals(); int numXVerts = TerrainGlobals.getTerrain().getNumXVerts(); int numZVerts = TerrainGlobals.getTerrain().getNumZVerts(); Matrix worldToProj = wd.workdToProj; for (int x = 0; x < numXVerts; x++) { for (int z = 0; z < numZVerts; z++) { int index = z + x * numXVerts; //only cast rays towards the light.. Vector3 nrm = -normals[index]; if (BMathLib.Dot(ref nrm, ref wd.rayDir) < 0) { continue; } Vector3 pos = TerrainGlobals.getTerrain().getPos(x, z); Vector4 vPos = new Vector4(pos.X, pos.Y, pos.Z, 1); Vector4 rPos = BMathLib.vec4Transform(ref vPos, ref worldToProj); rPos = rPos * (1.0f / rPos.W); float depth = rPos.Z;// +0.07f;//CLM this matches the shader... if (rPos.X < -1 || rPos.X >= 1 || rPos.Y < -1 || rPos.Y >= 1) { continue; } //grab our location in the depthGrid (screen space) float xPos = BMathLib.Clamp(0.5f * rPos.Y + 0.5f, 0, 1); float yPos = BMathLib.Clamp(0.5f * rPos.X + 0.5f, 0, 1); int xGridLoc = (int)((wd.imgWidth - 1) * (1 - xPos)); int yGridLoc = (int)((wd.imgHeight - 1) * (yPos)); if (!wd.fragList[xGridLoc, yGridLoc].anyValueSmaller(depth)) { AOVals[index] += wd.rcpNumSamples; Debug.Assert(AOVals[index] <= 1); } } } } }
void finalGatherWorker(object workerObjectDat) { gatherWorkerData workerDat = workerObjectDat as gatherWorkerData; int numXVerts = TerrainGlobals.getTerrain().getNumXVerts(); Matrix worldToProj = workerDat.workdToProj; for (int x = workerDat.minXVert; x < workerDat.maxXVert; x++) { for (int z = workerDat.minZVert; z < workerDat.maxZVert; z++) { //only cast rays towards the light.. Vector3 nrm = -TerrainGlobals.getTerrain().getNormal(x, z); if (BMathLib.Dot(ref nrm, ref workerDat.rayDir) < 0) { continue; } Vector3 pos = TerrainGlobals.getTerrain().getPos(x, z); Vector4 vPos = new Vector4(pos.X, pos.Y, pos.Z, 1); Vector4 rPos = BMathLib.vec4Transform(ref vPos, ref worldToProj); rPos = rPos * (1.0f / rPos.W); float depth = rPos.Z; if (rPos.X < -1 || rPos.X >= 1 || rPos.Y < -1 || rPos.Y >= 1) { continue; } //grab our location in the depthGrid (screen space) float xPos = BMathLib.Clamp(0.5f * rPos.Y + 0.5f, 0, 1); float yPos = BMathLib.Clamp(0.5f * rPos.X + 0.5f, 0, 1); int xGridLoc = (int)((workerDat.imgWidth - 1) * (1 - xPos)); int yGridLoc = (int)((workerDat.imgHeight - 1) * (yPos)); if (workerDat.fragList[xGridLoc, yGridLoc] != null && !workerDat.fragList[xGridLoc, yGridLoc].anyValueSmaller(depth)) { int index = z + x * numXVerts; Interlocked.Increment(ref AmbientOcclusion.AOIncrements[index]); } } } Interlocked.Increment(ref AmbientOcclusion.IncrementCount); workerDat.destroy(); workerDat = null; workerObjectDat = null; }
public void calcualteAO(eAOQuality quality, bool renderWorldObjects, ref float totalTime, ref float peelTime, ref float gatherTime, int startSampleIndex, int endSampleIndex) { totalTime = 0; peelTime = 0; gatherTime = 0; DateTime n = DateTime.Now; //should we multithread? if (CoreGlobals.mProcessorInfo.NumLogicalProcessors > 1) { mUsingMultithreading = true; AmbientOcclusion.IncrementCount = 0; AOIncrements = new int[TerrainGlobals.getTerrain().getNumXVerts() * TerrainGlobals.getTerrain().getNumZVerts()]; } else { mUsingMultithreading = false; } int width = 512; int height = 512; int numXSegments = 1; int numYSegments = 1; //special settings for Final mode if (quality == eAOQuality.cAO_Final) { numXSegments = 4; //what should these values really be? numYSegments = 4; // they should scale, so that huge terrain gets more segments.. // width = 1024; // height = 1024; // mUsingMultithreading = false; } //clear any handles available so we can use them as we see fit! TerrainGlobals.getTerrain().getQuadNodeRoot().clearVisibleDatHandle(); TerrainGlobals.getVisual().destroyAllHandles(); TerrainGlobals.getVisual().setHandleGenMode(true); blackoutAO(); //so we can gather light data Vector3 mWorldMin = Vector3.Empty; Vector3 mWorldMax = Vector3.Empty; BBoundingBox worldBounds = new BBoundingBox(); calculateWorldBounds(ref mWorldMin, ref mWorldMax, ref worldBounds, renderWorldObjects); mPeeler.init(width, height); Vector3 rayDir = -BMathLib.unitY; rayDir.Normalize(); Matrix worldToView = Matrix.Identity; Matrix viewToProj = Matrix.Identity; int numDesiredSamples = (int)Math.Sqrt((int)quality); int startIndex = 0; int endIndex = numDesiredSamples; List <Vector3> rayDirs = null; if (startSampleIndex != -1) { rayDirs = giveStratifiedOnHemiSphere(0, numDesiredSamples, false); startIndex = startSampleIndex; endIndex = Math.Min(endSampleIndex, rayDirs.Count); } else { rayDirs = giveStratifiedOnHemiSphere(0, numDesiredSamples, true); startIndex = 0; endIndex = rayDirs.Count; } float rcpNumSamples = 1.0f / (float)(rayDirs.Count); int numSamples = endIndex - startIndex; //begin our peel & gather process... bool ownBeginScene = !BRenderDevice.isInSceneRender(); if (ownBeginScene) { BRenderDevice.getDevice().BeginScene(); } for (int dc = startIndex; dc < endIndex; dc++) { //choose one rayDir = -rayDirs[dc]; computeWorldToView(ref rayDir, ref mWorldMin, ref mWorldMax, out worldToView); for (int x = 0; x < numXSegments; x++) { for (int y = 0; y < numYSegments; y++) { //calculate our lightspace projection matrix computeViewToProj(ref rayDir, worldBounds, worldToView, out viewToProj, numXSegments, numYSegments, x, y); //use worker threads.. gatherWorkerData wd = new gatherWorkerData(0, 0, TerrainGlobals.getTerrain().getNumXVerts(), TerrainGlobals.getTerrain().getNumZVerts(), width, height, rayDir, worldToView * viewToProj, rcpNumSamples); mPeeler.mDepthArray = null; mPeeler.mDepthArray = wd.fragList; //create our depth layers DateTime dpn = DateTime.Now; mPeeler.depthPeel(ref rayDir, ref worldToView, ref viewToProj, renderWorldObjects); TimeSpan dpts = DateTime.Now - dpn; peelTime += (float)dpts.TotalMinutes; //do final gathering (AO SPECIFIC) if (mUsingMultithreading) { finalGatherMultiThreaded(wd); } else { DateTime fgn = DateTime.Now; finalGatherSingleThreaded(wd); TimeSpan fgts = DateTime.Now - fgn; gatherTime += (float)fgts.TotalMinutes; } wd = null; mPeeler.mDepthArray = null; } } } if (ownBeginScene) { BRenderDevice.getDevice().EndScene(); } mPeeler.destroy(); TerrainGlobals.getVisual().setHandleGenMode(false); //if we're using multithreading, wait for all our worker threads to finish. if (mUsingMultithreading) { while (AmbientOcclusion.IncrementCount <= numSamples - 1) { Thread.Sleep(1); } float[] AOVals = TerrainGlobals.getEditor().getAmbientOcclusionValues(); for (int i = 0; i < AOIncrements.Length; i++) { AOVals[i] = AOIncrements[i] * rcpNumSamples; } } TimeSpan ts = DateTime.Now - n; totalTime = (float)ts.TotalMinutes; }
void finalGatherMultiThreaded(gatherWorkerData wd) { ThreadPool.QueueUserWorkItem(new WaitCallback(finalGatherWorker), wd); wd = null; }