⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 depthoffield.cpp

📁 游戏编程精华02-含有几十个游戏编程例子
💻 CPP
📖 第 1 页 / 共 4 页
字号:
{
    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 + -