📄 depthoffield.cpp
字号:
{
float inputx, inputy, inputz;
float distance, focusDistance, focalLength;
float minDistance, maxDistance;
float interpolator, circle;
DWORD dInterpolator, output;
FDebug("Camera parameters:\n");
FDebug("\tF-Stop: %7.2f \n", mFStop);
FDebug("\tFocal-Length: %6.1fmm\n", 10.0f*mFocalLength);
FDebug("\tFocus-Distance: %7.2fm \n\n", mFocusDistance/100.0f);
D3DSURFACE_DESC ddsd;
mpTextureFiltered[0]->GetLevelDesc(0, &ddsd);
float const c0 = kFilmDimension/static_cast<float>( max(ddsd.Width, ddsd.Height) );
float const ratio1 = pow(kBlurFactor, kNumOfFilterSteps);
float const ratio2 = pow(kBlurFactor, 2.0f*kNumOfFilterSteps);
// lock the volume texture
D3DLOCKED_BOX locked;
D3DLOCKED_RECT lockedRect;
if (mbUsesVolumes)
mpVolCircleOfConfusionLookup->LockBox( 0, &locked, NULL, 0);
else
mpCircleOfConfusionLookup->LockRect( 0, &lockedRect, NULL, 0);
// Here we compute the circle of confusion look-up table
// The x-dimension represents distance to the camera.
// Input ranges from 0 to 1 with 0 meaning minDistance
// and 1 means maximum distance .
// So we compute
// distance = input*(MaxDist-minDist) + minDistance // this is actual distance to camera
// circle of confusion formula is:
// circle = (focusDistance/distance - 1) * focalLength*focalLength /(FStop*(focusDistance - focalLength))
// circle = abs(circle)
//
// The y-dimension represents focusDistance
// Input ranges from 0 to 1, with 0 meaning minFocusDistance (kCloseClip)
// and 1 meaning maxFocusDistance (farClip)
//
// The z-dimension represents focalLength
// Input ranges from 0 to 1, with 0 meaning minFocalLength
// and 1 meaning maxFocalLength
//
// At the heart of it all, we figure out what the min- and max-distance is for
// the depth of field for a circle of confusion of ratio2*c0. Stuff *blurrier*
// than that we cannot represent anyway, so there is no need to waste
// interpolator-range on it. Then we simply iterate from min- to max-distance
// and compute the circle of confusion for that particular distance.
//
// The circle of confusion diameter computed above is then mapped into an
// interpolator ranging from 0 to 1, with 0 corresponding to circles of diameter
// c0 or less, 0.5 corresponding to diameters ratio1*c0, and 1 corresponding to
// diameters ratio2*c0 or more.
//
// All formulas come from "Photographic Lenses Tutorial" by David M. Jacobson
// (www.graflex.org/lenses/photographic-lenses-tutorial.html) -- a most excellent
// reference guide.
int x, y, z;
DWORD *pBase = static_cast<DWORD *>((mbUsesVolumes) ? locked.pBits : lockedRect.pBits);
DWORD *pCurrent = pBase;
for (z = 0; z < ((mbUsesVolumes) ? kConfusionLookupDepth : 1); ++z)
{
inputz = static_cast<float>(z)/static_cast<float>(kConfusionLookupDepth-1);
focalLength = (kMaxFocalLength - kMinFocalLength) * inputz + kMinFocalLength;
if (! mbUsesVolumes)
focalLength = mFocalLength;
for (y = 0; y < kConfusionLookupHeight; ++y)
{
inputy = static_cast<float>(y)/static_cast<float>(kConfusionLookupHeight-1);
focusDistance = (kMaxFocusDistance - kMinFocusDistance) * inputy + kMinFocusDistance;
float const hyperFocal = focalLength*focalLength/(mFStop * ratio2 * c0);
float const denominator = hyperFocal - (focusDistance - focalLength);
minDistance = 0.01f * hyperFocal * focusDistance/(hyperFocal + focusDistance - focalLength);
minDistance = max(minDistance, kCloseClip);
maxDistance = kFarClip;
if (denominator > 0.0f)
maxDistance = min(maxDistance, 0.01f * focusDistance * (hyperFocal/denominator));
if (minDistance >= maxDistance)
maxDistance = minDistance + minDistance - maxDistance;
for (x = 0; x < kConfusionLookupWidth; ++x)
{
inputx = static_cast<float>(x)/static_cast<float>(kConfusionLookupWidth-1);
distance = (maxDistance - minDistance) * inputx + minDistance;
distance = 100.0f * distance; // convert from meters to cm
circle = (focusDistance/distance - 1.0f) * focalLength * focalLength;
circle = circle/(mFStop * (focusDistance - focalLength));
circle = fabs(circle);
if (circle <= c0)
{
interpolator = 0.0f;
}
else if (circle <= ratio1*c0)
{ // interpolator is in 0.0 .. 0.5 range
interpolator = (circle-c0)/((ratio1-1.0f)*c0);
interpolator = max(0.0f, interpolator);
interpolator = min(1.0f, interpolator);
interpolator = 0.5f * interpolator;
}
else // interpolator is in 0.5 .. 1.0 range
{
interpolator = (circle-ratio1*c0)/((ratio2-ratio1)*c0);
interpolator = max(0.0f, interpolator);
interpolator = min(1.0f, interpolator);
interpolator = 0.5f * (1.0f + interpolator);
}
dInterpolator = static_cast<DWORD>(interpolator * 255.0f) & 0xff;
output = (dInterpolator << 24) | (dInterpolator << 16)
| (dInterpolator << 8) | (dInterpolator << 0);
*pCurrent++ = output;
}
}
}
// done generating texture: unlock it
if (mbUsesVolumes)
mpVolCircleOfConfusionLookup->UnlockBox( 0 );
else
mpCircleOfConfusionLookup->UnlockRect( 0 );
return S_OK;
}
HRESULT CDepthOfField::UpdateCameraParameters()
{
// c0 is the acceptable circle of confusion diameter (in cm) for the
// original resolution texture (in essence it is the size of a pixel in cm).
// The ratio is how much we can blur this original circle of confusion:
// each 9-sample box-filter operation enlarges the circle of confusion
// diameter. Because we are generating two blur targets, we get:
// ratio = enlargement^(2*kNumOfFilterSteps)
D3DSURFACE_DESC ddsd;
mpTextureFiltered[0]->GetLevelDesc(0, &ddsd);
float const c0 = kFilmDimension/static_cast<float>( max(ddsd.Width, ddsd.Height) );
float const ratio = pow(kBlurFactor, 2.0f*kNumOfFilterSteps);
// ratio * c0 is the most blurred circle of confusion which we are able to
// represent. Thus, lets compute the hyperfocal distance h with respect to
// ratio * c0, and derive MinDist and MaxDist with respect to h. Then we
// simply scale all distances in the vertex shader with respect to these
// minDist and maxDist.
// Note: 0.01 multiplier transforms from cm to meters.
float const hyperFocal = mFocalLength*mFocalLength/(mFStop * ratio * c0);
float const denominator = hyperFocal - (mFocusDistance - mFocalLength);
float minDistance, maxDistance;
minDistance = 0.01f * hyperFocal * mFocusDistance/(hyperFocal + mFocusDistance - mFocalLength);
minDistance = max(minDistance, kCloseClip);
// far distance is only valid if the denominator is > 0 (otherwise it is infinite)
maxDistance = kFarClip; // this value is an ok representation for infinity
if (denominator > 0.0f)
maxDistance = min(maxDistance, 0.01f * hyperFocal * mFocusDistance/denominator);
if (minDistance >= maxDistance)
maxDistance = minDistance + minDistance - maxDistance;
// write min- and max-distance to vertex shader memory: the vertex shader
// converts and stores distance-to-camera to a 0 thru 1 range (used to
// look up circle of confusion in the texture stage)
float const minMaxDistance[4] = {minDistance/(maxDistance-minDistance),
1.0f /(maxDistance-minDistance),
2.0f,
1.0f};
m_pD3DDev->SetVertexShaderConstant(CV_MINMAX_DIST, minMaxDistance, 1);
// write the current focus distance and focla length to memory
// (normalized to between 0 and 1)
float const focusConstants[4] = {(mFocusDistance-kMinFocusDistance)/(kMaxFocusDistance-kMinFocusDistance),
(mFocalLength -kMinFocalLength) /(kMaxFocalLength -kMinFocalLength),
0.0f,
0.0f };
m_pD3DDev->SetVertexShaderConstant(CV_FOCUS_CONST, focusConstants, 1);
return S_OK;
}
void CDepthOfField::MouseMove(HWND hWnd, int x, int y)
{
mpMouseUI->OnMouseMove(x, y);
}
void CDepthOfField::MouseButton(HWND hWnd, eButtonID button, bool bDown, int x, int y)
{
if(button == MOUSE_LEFTBUTTON)
{
if(bDown)
mpMouseUI->OnLButtonDown(x, y);
else
mpMouseUI->OnLButtonUp(x, y);
}
}
void CDepthOfField::Keyboard(DWORD dwKey, UINT nFlags, bool bDown)
{
eEBKeyAction Action = TranslateEffectKey(dwKey, nFlags, bDown);
m_bKey['P'] = m_bKey['U'] = m_bKey['L'] = m_bKey['K'] = false;
switch ( Action )
{
case EB_HELP:
{
::MessageBoxEx( NULL, " Help : F1 - Help \n\n Home - Reset To Defaults \n\n W - Wireframe Toggle \n\n I/O - Zoom Camera In/Out\n\n U/P - Change Focus-Distance\n\n K/L or PageUp/PageDn - Change F-Stop\n\n Left Button & Mouse - Rotate Camera\n\n Shift Left Button & Mouse - Pan Camera \n\n Ctrl Left Button & Mouse - Move Camera In & Out\n\n",
"Help", MB_ICONINFORMATION | MB_TASKMODAL, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ) );
}
break;
case EB_WIREFRAME:
{
mbWireFrame = !mbWireFrame;
m_dwEffectDirtyFlags |= EBEFFECT_DIRTY_PUBLICSTATE;
}
break;
case EB_RESET:
{
mpMouseUI->Reset();
mFStop = 1.0f;
mFocalLength = 20.0f;
mFocusDistance = 2536.0f;
m_dwEffectDirtyFlags |= EBEFFECT_DIRTY_PUBLICSTATE;
UpdateCameraParameters();
GenerateCircleOfConfusionTexture();
}
break;
case EB_ADD:
m_bKey['P'] = true;
break;
case EB_SUBTRACT:
m_bKey['U'] = true;
break;
case EB_PAGEUP:
m_bKey['L'] = true;
break;
case EB_PAGEDOWN:
m_bKey['K'] = true;
break;
default:
m_bKey[dwKey] = bDown;
break;
}
}
void CDepthOfField::SetViewFromKeyboardInput(EBTimer* pTimer)
{
float const kSpeed = pTimer->GetSecsPerFrame() * 10.0f;
float const moveSpeed = kSpeed * 2.0f;
float const fStopChange = kSpeed * 0.5f; // multiply fStop by this value on every key-press
float const minFStop = 0.6f;
float const maxFStop = 22.0f;
float const focusDistanceChange = kSpeed * 0.02f; // multiply focusDistance by this value on every key-press
float const focalLengthChange = kSpeed * 0.05f; // multiply focalLength by this value on every key-press
D3DXVECTOR3 const yVector(0.0f, 1.0f, 0.0f);
float const h = D3DXVec3Dot(&yVector, &mCamLookDirection);
D3DXVECTOR3 xzProjectedLookDirection(mCamLookDirection - h*yVector);
D3DXVec3Normalize(&xzProjectedLookDirection, &xzProjectedLookDirection);
D3DXVECTOR3 rightVector;
D3DXVec3Cross(&rightVector, &yVector, &xzProjectedLookDirection);
bool regenerateTexture = false;
bool updateConstants = false;
if(m_bKey[VK_UP] || m_bKey[VK_NUMPAD8])
mpMouseUI->Translate(moveSpeed*xzProjectedLookDirection);
if(m_bKey[VK_DOWN] || m_bKey[VK_NUMPAD2])
mpMouseUI->Translate(- moveSpeed*xzProjectedLookDirection);
if(m_bKey[VK_LEFT] || m_bKey[VK_NUMPAD4])
mpMouseUI->Translate(- 0.25f*moveSpeed*rightVector);
if(m_bKey[VK_RIGHT] || m_bKey[VK_NUMPAD6])
mpMouseUI->Translate(0.25f*moveSpeed*rightVector);
if(m_bKey[VK_PRIOR])
mpMouseUI->Translate(0.25f*moveSpeed*yVector);
if(m_bKey[VK_NEXT])
mpMouseUI->Translate(- 0.25f*moveSpeed*yVector);
D3DXVECTOR4 position;
D3DXVec3Transform(&position, &D3DXVECTOR3(0.0f, 1.0f, -25.0f), &mpMouseUI->GetTranslationMatrix());
mCamPosition = D3DXVECTOR3(position.x, max(0.1f, position.y), position.z);
if (m_bKey['P'])
{
mFocusDistance += focusDistanceChange*mFocusDistance;
if (mFocusDistance > kMaxFocusDistance)
mFocusDistance = kMaxFocusDistance;
updateConstants = true;
}
if (m_bKey['U'])
{
mFocusDistance -= focusDistanceChange*mFocusDistance;
if (mFocusDistance < kMinFocusDistance)
mFocusDistance = kMinFocusDistance;
updateConstants = true;
}
if (m_bKey['L'])
{
mFStop += fStopChange*mFStop;
if (mFStop > maxFStop)
mFStop = maxFStop;
regenerateTexture = true;
}
if (m_bKey['K'])
{
mFStop -= fStopChange*mFStop;
if (mFStop < minFStop )
mFStop = minFStop ;
regenerateTexture = true;
}
if (m_bKey['I'])
{
mFocalLength += focalLengthChange*mFocalLength;
if (mFocalLength > kMaxFocalLength)
mFocalLength = kMaxFocalLength;
updateConstants = true;
if (! mbUsesVolumes)
regenerateTexture = true;
}
if (m_bKey['O'])
{
mFocalLength -= focalLengthChange*mFocalLength;
if (mFocalLength < kMinFocalLength)
mFocalLength = kMinFocalLength;
updateConstants = true;
if (! mbUsesVolumes)
regenerateTexture = true;
}
// write constants
if (regenerateTexture || updateConstants)
UpdateCameraParameters();
// regenerate texture and print status
if (regenerateTexture)
GenerateCircleOfConfusionTexture();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -