void FGLRenderBuffers::CreateBloom(int width, int height)
{
	ClearBloom();
	
	// No scene, no bloom!
	if (width <= 0 || height <= 0)
		return;

	int bloomWidth = (width + 1) / 2;
	int bloomHeight = (height + 1) / 2;
	for (int i = 0; i < NumBloomLevels; i++)
	{
		auto &level = BloomLevels[i];
		level.Width = (bloomWidth + 1) / 2;
		level.Height = (bloomHeight + 1) / 2;

		level.VTexture = Create2DTexture("Bloom.VTexture", GL_RGBA16F, level.Width, level.Height);
		level.HTexture = Create2DTexture("Bloom.HTexture", GL_RGBA16F, level.Width, level.Height);
		level.VFramebuffer = CreateFrameBuffer("Bloom.VFramebuffer", level.VTexture);
		level.HFramebuffer = CreateFrameBuffer("Bloom.HFramebuffer", level.HTexture);

		bloomWidth = level.Width;
		bloomHeight = level.Height;
	}
}
void FGLRenderBuffers::CreateExposureLevels(int width, int height)
{
	ClearExposureLevels();

	int i = 0;
	do
	{
		width = MAX(width / 2, 1);
		height = MAX(height / 2, 1);

		FString textureName, fbName;
		textureName.Format("Exposure.Texture%d", i);
		fbName.Format("Exposure.Framebuffer%d", i);
		i++;

		FGLExposureTextureLevel level;
		level.Width = width;
		level.Height = height;
		level.Texture = Create2DTexture(textureName, GL_R32F, level.Width, level.Height);
		level.Framebuffer = CreateFrameBuffer(fbName, level.Texture);
		ExposureLevels.Push(level);
	} while (width > 1 || height > 1);

	ExposureTexture = Create2DTexture("Exposure.CameraTexture", GL_R32F, 1, 1);
	ExposureFB = CreateFrameBuffer("Exposure.CameraFB", ExposureTexture);

	FirstExposureFrame = true;
}
Пример #3
0
bool VROpenVRNode::SetupStereoRenderTargets()
{
    if ( !m_pHMD )
        return false;

    m_pHMD->GetRecommendedRenderTargetSize( &m_nRenderWidth, &m_nRenderHeight );

    CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, leftEyeDesc );
    CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, rightEyeDesc );

    return true;
}
Пример #4
0
void FGLRenderBuffers::CreateShadowMap()
{
	if (mShadowMapTexture.handle != 0 && gl_shadowmap_quality == mCurrentShadowMapSize)
		return;

	ClearShadowMap();

	GLint activeTex, textureBinding, frameBufferBinding;
	glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
	glActiveTexture(GL_TEXTURE0);
	glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
	glGetIntegerv(GL_FRAMEBUFFER_BINDING, &frameBufferBinding);

	mShadowMapTexture = Create2DTexture("ShadowMap", GL_R32F, gl_shadowmap_quality, 1024);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	mShadowMapFB = CreateFrameBuffer("ShadowMapFB", mShadowMapTexture);

	glBindTexture(GL_TEXTURE_2D, textureBinding);
	glActiveTexture(activeTex);
	glBindFramebuffer(GL_FRAMEBUFFER, frameBufferBinding);

	mCurrentShadowMapSize = gl_shadowmap_quality;
}
Пример #5
0
void Pass::Bind()
{
    if (m_OutputTextures.size() > 0 && m_FrameBuffer == 0)
        CreateFrameBuffer();

    glBindFramebuffer(GL_FRAMEBUFFER, m_FrameBuffer);

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(EnumUtil::Instance()->CompareModeToUint(m_CompareMode));

    if (m_BlendMode != BlendMode::REPLACE)
    {
        glEnable(GL_BLEND);
        glBlendFunc(EnumUtil::Instance()->BlendModeSrc(m_BlendMode),
                    EnumUtil::Instance()->BlendModeDest(m_BlendMode));
    }
    else
    {
        glDisable(GL_BLEND);
    }

    if (m_CullMode != CullMode::NONE)
    {
        glEnable(GL_CULL_FACE);
        glCullFace(EnumUtil::Instance()->CullModeToUint(m_CullMode).second);
    }
    else
    {
        glDisable(GL_CULL_FACE);
    }
}
Пример #6
0
	// Inheritance exigences
	void TGBufferCanvas::Init()
	{
		// Drawable data
		m_output.width = m_width; 
		m_output.height = m_height; 

		// Creating the main frame buffer
		m_frameBuffer = CreateFrameBuffer();
		BindFrameBuffer(m_frameBuffer);
		// Creating the textures
		// Memory allocation
		m_output.buffers.resize(5);

		// The abledo buffer
		TTextureInfo& albedo = m_output.buffers[0];
		albedo.name = "albedo";
		albedo.type = TTextureNature::COLOR;
		albedo.offset = 0;
		CreateTexture(albedo, m_width, m_height);
 		BindToFrameBuffer(albedo);

 		// The normal buffer
		TTextureInfo& normal = m_output.buffers[1];
		normal.name = "normal";
		normal.type = TTextureNature::COLOR;
		normal.offset = 1;
		CreateTexture(normal, m_width, m_height);
 		BindToFrameBuffer(normal);

 		// The specular buffer
		TTextureInfo& specular = m_output.buffers[2];
		specular.name = "specular";
		specular.type = TTextureNature::COLOR;
		specular.offset = 2;
		CreateTexture(specular, m_width, m_height);
 		BindToFrameBuffer(specular);

 		// Position Buffer
		TTextureInfo& position = m_output.buffers[3];
		position.name = "position";
		position.type = TTextureNature::COLOR;
		position.offset = 3;
		CreateTexture(position, m_width, m_height);
 		BindToFrameBuffer(position);

 		// Depth buffer
		TTextureInfo& depth = m_output.buffers[4];
		depth.name = "depth";
		depth.type = TTextureNature::DEPTH;
		depth.offset = 4;
		CreateTexture(depth, m_width, m_height);
 		BindToFrameBuffer(depth);
 		// Making sure everything is OK
 		CheckFrameBuffer();
 		UnBindFrameBuffer();
	}
Пример #7
0
void FGLRenderBuffers::CreateScene(int width, int height, int samples)
{
	ClearScene();

	if (samples > 1)
		mSceneMultisample = CreateRenderBuffer("SceneMultisample", GL_RGBA16F, samples, width, height);

	mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, samples, width, height);
	mSceneFB = CreateFrameBuffer("SceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneDepthStencil, samples > 1);
}
void FGLRenderBuffers::CreateAmbientOcclusion(int width, int height)
{
	ClearAmbientOcclusion();

	if (width <= 0 || height <= 0)
		return;

	AmbientWidth = (width + 1) / 2;
	AmbientHeight = (height + 1) / 2;
	LinearDepthTexture = Create2DTexture("LinearDepthTexture", GL_R32F, AmbientWidth, AmbientHeight);
	AmbientTexture0 = Create2DTexture("AmbientTexture0", GL_RG16F, AmbientWidth, AmbientHeight);
	AmbientTexture1 = Create2DTexture("AmbientTexture1", GL_RG16F, AmbientWidth, AmbientHeight);
	LinearDepthFB = CreateFrameBuffer("LinearDepthFB", LinearDepthTexture);
	AmbientFB0 = CreateFrameBuffer("AmbientFB0", AmbientTexture0);
	AmbientFB1 = CreateFrameBuffer("AmbientFB1", AmbientTexture1);

	// Must match quality enum in FSSAOShader::GetDefines
	double numDirections[NumAmbientRandomTextures] = { 2.0, 4.0, 8.0 };

	std::mt19937 generator(1337);
	std::uniform_real_distribution<double> distribution(0.0, 1.0);
	for (int quality = 0; quality < NumAmbientRandomTextures; quality++)
	{
		int16_t randomValues[16 * 4];

		for (int i = 0; i < 16; i++)
		{
			double angle = 2.0 * M_PI * distribution(generator) / numDirections[quality];
			double x = cos(angle);
			double y = sin(angle);
			double z = distribution(generator);
			double w = distribution(generator);

			randomValues[i * 4 + 0] = (int16_t)clamp(x * 32767.0, -32768.0, 32767.0);
			randomValues[i * 4 + 1] = (int16_t)clamp(y * 32767.0, -32768.0, 32767.0);
			randomValues[i * 4 + 2] = (int16_t)clamp(z * 32767.0, -32768.0, 32767.0);
			randomValues[i * 4 + 3] = (int16_t)clamp(w * 32767.0, -32768.0, 32767.0);
		}

		AmbientRandomTexture[quality] = Create2DTexture("AmbientRandomTexture", GL_RGBA16_SNORM, 4, 4, randomValues);
	}
}
void FGLRenderBuffers::CreatePipeline(int width, int height)
{
	ClearPipeline();
	ClearEyeBuffers();

	for (int i = 0; i < NumPipelineTextures; i++)
	{
		mPipelineTexture[i] = Create2DTexture("PipelineTexture", GL_RGBA16F, width, height);
		mPipelineFB[i] = CreateFrameBuffer("PipelineFB", mPipelineTexture[i]);
	}
}
void FGLRenderBuffers::CreateScene(int width, int height, int samples, bool needsSceneTextures)
{
	ClearScene();

	if (samples > 1)
	{
		if (needsSceneTextures)
		{
			mSceneMultisample = Create2DMultisampleTexture("SceneMultisample", GL_RGBA16F, width, height, samples, false);
			mSceneDepthStencil = Create2DMultisampleTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height, samples, false);
			mSceneFog = Create2DMultisampleTexture("SceneFog", GL_RGBA8, width, height, samples, false);
			mSceneNormal = Create2DMultisampleTexture("SceneNormal", GL_RGB10_A2, width, height, samples, false);
			mSceneFB = CreateFrameBuffer("SceneFB", mSceneMultisample, 0, 0, mSceneDepthStencil, true);
			mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mSceneMultisample, mSceneFog, mSceneNormal, mSceneDepthStencil, true);
		}
		else
		{
			mSceneMultisample = CreateRenderBuffer("SceneMultisample", GL_RGBA16F, width, height, samples);
			mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height, samples);
			mSceneFB = CreateFrameBuffer("SceneFB", mSceneMultisample, mSceneDepthStencil, true);
			mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mSceneMultisample, mSceneDepthStencil, true);
		}
	}
	else
	{
		if (needsSceneTextures)
		{
			mSceneDepthStencil = Create2DTexture("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height);
			mSceneFog = Create2DTexture("SceneFog", GL_RGBA8, width, height);
			mSceneNormal = Create2DTexture("SceneNormal", GL_RGB10_A2, width, height);
			mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], 0, 0, mSceneDepthStencil, false);
			mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mPipelineTexture[0], mSceneFog, mSceneNormal, mSceneDepthStencil, false);
		}
		else
		{
			mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, width, height);
			mSceneFB = CreateFrameBuffer("SceneFB", mPipelineTexture[0], mSceneDepthStencil, false);
			mSceneDataFB = CreateFrameBuffer("SceneGBufferFB", mPipelineTexture[0], mSceneDepthStencil, false);
		}
	}
}
Пример #11
0
void MeshEntity::SetMesh(CTSTR lpMesh)
{
    traceIn(MeshEntity::SetMesh);

    if(!mesh)
    {
        mesh = RM->GetMesh(lpMesh);

        if(!MaterialList.Num())
        {
            MaterialList.SetSize(mesh->DefaultMaterialList.Num());
            for(int i=0; i<MaterialList.Num(); i++)
            {
                String resName = mesh->DefaultMaterialList[i].name; //converting from utf to wide
                MaterialList[i] = GetMaterial(String(resName));
            }
        }
    }

    if(bCastCompositeShadow && !bStaticGeometry && level->UsesLightmaps())
    {
        compositeShadow = CreateFrameBuffer(64, 64, GS_RGBA, FALSE);
        shadowDecal = CreateObjectParam(ShadowDecal, this);
        shadowDecal->texture = compositeShadow;
        shadowDecal->bRenderable = FALSE;
        shadowDecal->SetPos(mesh->bounds.GetCenter());
        shadowDecal->shadowRot = AxisAngle(1.0f, 0.0f, 0.0f, RAD(-80.0f));
        shadowDecal->shadowRot *= AxisAngle(0.0f, 1.0f, 0.0f, RAD(-10.0f));
        shadowDecal->decalColor.Set(1.0f, 1.0f, 1.0f, 0.75f);
        shadowDecal->Attach(this);
    }

    VertBuffer = mesh->VertBuffer;
    VBData *vbd = VertBuffer->GetData();
    VertList = vbd->VertList.Array();

    FaceNormalList = NULL;

    bounds = mesh->bounds;

    traceOut;
}
void FGLRenderBuffers::CreateEyeBuffers(int eye)
{
	if (mEyeFBs.Size() > eye)
		return;

	GLint activeTex, textureBinding, frameBufferBinding;
	glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
	glActiveTexture(GL_TEXTURE0);
	glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
	glGetIntegerv(GL_FRAMEBUFFER_BINDING, &frameBufferBinding);

	while (mEyeFBs.Size() <= eye)
	{
		GLuint texture = Create2DTexture("EyeTexture", GL_RGBA16F, mWidth, mHeight);
		mEyeTextures.Push(texture);
		mEyeFBs.Push(CreateFrameBuffer("EyeFB", texture));
	}

	glBindTexture(GL_TEXTURE_2D, textureBinding);
	glActiveTexture(activeTex);
	glBindFramebuffer(GL_FRAMEBUFFER, frameBufferBinding);
}
Пример #13
0
//-----------------------------------------------------------------------
GraphicsContextGL4::GraphicsContextGL4(
    std::shared_ptr<OpenGLContext> const& openGLContextIn,
    std::weak_ptr<GameWindow> windowIn)
    : nativeContext(openGLContextIn)
    , gameWindow(std::move(windowIn))
    , needToApplyInputLayout(true)
    , needToApplyPipelineState(true)
{
    auto version = reinterpret_cast<char const*>(glGetString(GL_VERSION));
    Log::Stream(LogLevel::Internal) << "OpenGL Version: " << version;

    auto capabilities = GetCapabilities();
    if (capabilities.SamplerSlotCount > 0) {
        textures.resize(capabilities.SamplerSlotCount);
    }

    glFrontFace(GL_CW);
    POMDOG_CHECK_ERROR_GL4("glFrontFace");

    frameBuffer = CreateFrameBuffer();
    primitiveTopology = ToPrimitiveTopology(PrimitiveTopology::TriangleList);
}
Пример #14
0
DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old)
{
	static int retry = 0;
	static int owidth, oheight;

	BaseWinFB *fb;
	PalEntry flashColor;
	int flashAmount;

	LOG4 ("CreateFB %d %d %d %p\n", width, height, fullscreen, old);

	if (old != NULL)
	{ // Reuse the old framebuffer if its attributes are the same
		BaseWinFB *fb = static_cast<BaseWinFB *> (old);
		if (fb->Width == width &&
			fb->Height == height &&
			fb->Windowed == !fullscreen)
		{
			return old;
		}
		old->GetFlash (flashColor, flashAmount);
		old->ObjectFlags |= OF_YesReallyDelete;
		if (old == screen) screen = NULL;
		delete old;
	}
	else
	{
		flashColor = 0;
		flashAmount = 0;
	}

	if (D3D != NULL)
	{
		fb = new D3DFB (width, height, fullscreen);
	}
	else
	{
		fb = new DDrawFB (width, height, fullscreen);
	}
	LOG1 ("New fb created @ %p\n", fb);

	// If we could not create the framebuffer, try again with slightly
	// different parameters in this order:
	// 1. Try with the closest size
	// 2. Try in the opposite screen mode with the original size
	// 3. Try in the opposite screen mode with the closest size
	// This is a somewhat confusing mass of recursion here.

	while (fb == NULL || !fb->IsValid ())
	{
		static HRESULT hr;

		if (fb != NULL)
		{
			if (retry == 0)
			{
				hr = fb->GetHR ();
			}
			delete fb;

			LOG1 ("fb is bad: %08lx\n", hr);
		}
		else
		{
			LOG ("Could not create fb at all\n");
		}
		screen = NULL;

		LOG1 ("Retry number %d\n", retry);

		switch (retry)
		{
		case 0:
			owidth = width;
			oheight = height;
		case 2:
			// Try a different resolution. Hopefully that will work.
			I_ClosestResolution (&width, &height, 8);
			LOG2 ("Retry with size %d,%d\n", width, height);
			break;

		case 1:
			// Try changing fullscreen mode. Maybe that will work.
			width = owidth;
			height = oheight;
			fullscreen = !fullscreen;
			LOG1 ("Retry with fullscreen %d\n", fullscreen);
			break;

		default:
			// I give up!
			LOG3 ("Could not create new screen (%d x %d): %08lx", owidth, oheight, hr);
			I_FatalError ("Could not create new screen (%d x %d): %08lx", owidth, oheight, hr);
		}

		++retry;
		fb = static_cast<DDrawFB *>(CreateFrameBuffer (width, height, fullscreen, NULL));
	}
	retry = 0;

	fb->SetFlash (flashColor, flashAmount);

	return fb;
}
Пример #15
0
int main()
{
	eosFrameBuffer fb;
	if(eosFrameBuffer_Open(&fb)) {
		return 0;
	}

	CFrameBuffer bf = CreateFrameBuffer(1366, 768);

	struct timeval start, end;
 
    	long mtime, seconds, useconds;    
 
    	gettimeofday(&start, NULL);
 
	
	// Draw background rectangle.
	CRect bg_rect = CreateRect(100, 100, 200, 200);
	CColor bg_color = CreateColor(240, 0, 0);
	DrawRectangle(&bf, &bg_rect, &bg_color);

	// Draw rectangle contour.
	//CColor contour_color = CreateColor(0, 0, 0);
	//DrawRectangleContour(&bf, &bg_rect, &contour_color);

	// Draw diagonal line.
	//CPoint p0 = CPoint_Create(0, 499);
	//CPoint p1 = CPoint_Create(499, 0);
	//CColor p_color = CreateColor(255, 255, 0);
	//DrawLine(&bf, &p0, &p1, &p_color);

	// Draw char.
	CFont font = CreateFont("resources/test.myfont");
	//CPoint char_pos = CPoint_Create(300, 100);
	//CColor char_color = CreateColor(255, 255, 255);
	//DrawChar(&bf, &font, 'P', &char_pos, &char_color);

	// Draw text.
	CPoint str_pos = CPoint_Create(100, 200);
	CColor str_color = CreateColor(180, 180, 180);
	DrawString(&bf, &font, "Monster Truck", &str_pos, &str_color);

	// Draw text.
	CPoint str2_pos = CPoint_Create(100, 220);
	CColor str2_color = CreateColor(10, 180, 180);
	DrawString(&bf, &font, "Monster Truck", &str2_pos, &str2_color);

	// Get text width.
	//CString mst_str = CString_Create("Monster Truck");
	//printf("Monster Truck width : %d\n", GetStringXSize(&font, &mst_str));

	// Draw image resize.
	CImage image = CreateImageFromBitmap("resources/umbrella2.bmp");
	CPoint img_pos = CPoint_Create(300, 51);
	CSize img_size = CreateSize(500, 500);
	DrawImageResize(&bf, &image, &img_pos, &img_size);	

	eosFrameBuffer_Draw(&fb);
	eosFrameBuffer_DrawBackBuffer(&fb, &bf);
    	
	gettimeofday(&end, NULL);
    	seconds  = end.tv_sec  - start.tv_sec;
    	useconds = end.tv_usec - start.tv_usec;
    	mtime = seconds + useconds;
   	printf("Elapsed time: %d ms\n", mtime / 1000);

	gettimeofday(&start, NULL);
	
	DrawRectangle(&bf, &bg_rect, &bg_color);
	DrawString(&bf, &font, "Monster Truck", &str_pos, &str_color);
	DrawString(&bf, &font, "Monster Truck", &str2_pos, &str2_color);
	DrawImageResize(&bf, &image, &img_pos, &img_size);
	eosFrameBuffer_DrawBackBuffer(&fb, &bf);

	gettimeofday(&end, NULL);
	seconds  = end.tv_sec  - start.tv_sec;
        useconds = end.tv_usec - start.tv_usec;
        mtime = seconds + useconds;
        printf("Elapsed time: %d ms\n", mtime / 1000);

	eosFrameBuffer_Close(&fb);
	//printf("FIX:\n");
	//printf("FB len         : %d\n", m_FixInfo.smem_len);
	//printf("FB type        : %d\n", m_FixInfo.type);
	//printf("FB type aux    : %d\n", m_FixInfo.type_aux);
	//printf("FB visual      : %d\n", m_FixInfo.visual);
	//printf("FB xpanstep    : %d\n", m_FixInfo.xpanstep);
	//printf("FB ypanstep    : %d\n", m_FixInfo.ypanstep);
	//printf("FB ywrapstep   : %d\n", m_FixInfo.ywrapstep);
	//printf("FB line_length : %d\n", m_FixInfo.line_length);

	//printf("VAR:\n");
	//printf("Res            : %d %d\n", m_VarInfo.xres, m_VarInfo.yres);
	//printf("Res virtual    : %d %d\n", m_VarInfo.xres_virtual, m_VarInfo.yres_virtual);
	//printf("Offset         : %d %d\n", m_VarInfo.xoffset, m_VarInfo.yoffset);
	//printf("bits_per_pixel : %d\n", m_VarInfo.bits_per_pixel);
	//printf("grayscale      : %d\n", m_VarInfo.grayscale);
	return 0;
}
Пример #16
0
void FGLRenderBuffers::RenderEffect(const FString &name)
{
	if (hw_postprocess.Effects[name].Size() == 0)
		return;

	FGLDebug::PushGroup(name.GetChars());

	FGLPostProcessState savedState;

	for (const PPStep &step : hw_postprocess.Effects[name])
	{
		// Bind input textures
		for (unsigned int index = 0; index < step.Textures.Size(); index++)
		{
			savedState.SaveTextureBindings(index + 1);

			const PPTextureInput &input = step.Textures[index];
			int filter = (input.Filter == PPFilterMode::Nearest) ? GL_NEAREST : GL_LINEAR;
			int wrap = (input.Wrap == PPWrapMode::Clamp) ? GL_CLAMP : GL_REPEAT;

			switch (input.Type)
			{
			default:
			case PPTextureType::CurrentPipelineTexture:
				BindCurrentTexture(index, filter, wrap);
				break;

			case PPTextureType::NextPipelineTexture:
				I_FatalError("PPTextureType::NextPipelineTexture not allowed as input\n");
				break;

			case PPTextureType::PPTexture:
				GLTextures[input.Texture].Bind(index, filter, wrap);
				break;

			case PPTextureType::SceneColor:
				BindSceneColorTexture(index);
				break;

			case PPTextureType::SceneFog:
				BindSceneFogTexture(index);
				break;

			case PPTextureType::SceneNormal:
				BindSceneNormalTexture(index);
				break;

			case PPTextureType::SceneDepth:
				BindSceneDepthTexture(index);
				break;
			}
		}

		// Set render target
		switch (step.Output.Type)
		{
		default:
			I_FatalError("Unsupported postprocess output type\n");
			break;

		case PPTextureType::CurrentPipelineTexture:
			BindCurrentFB();
			break;

		case PPTextureType::NextPipelineTexture:
			BindNextFB();
			break;

		case PPTextureType::PPTexture:
			if (GLTextureFBs[step.Output.Texture])
				GLTextureFBs[step.Output.Texture].Bind();
			else
				GLTextureFBs[step.Output.Texture] = CreateFrameBuffer(step.Output.Texture.GetChars(), GLTextures[step.Output.Texture]);
			break;

		case PPTextureType::SceneColor:
			BindSceneFB(false);
			break;
		}

		// Set blend mode
		if (step.BlendMode.BlendOp == STYLEOP_Add && step.BlendMode.SrcAlpha == STYLEALPHA_One && step.BlendMode.DestAlpha == STYLEALPHA_Zero && step.BlendMode.Flags == 0)
		{
			glDisable(GL_BLEND);
		}
		else
		{
			// To do: support all the modes
			glEnable(GL_BLEND);
			glBlendEquation(GL_FUNC_ADD);
			if (step.BlendMode.SrcAlpha == STYLEALPHA_One && step.BlendMode.DestAlpha == STYLEALPHA_One)
				glBlendFunc(GL_ONE, GL_ONE);
			else
				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		}

		// Setup viewport
		glViewport(step.Viewport.left, step.Viewport.top, step.Viewport.width, step.Viewport.height);

		auto &shader = GLShaders[step.ShaderName];

		// Set uniforms
		if (step.Uniforms.Data.Size() > 0)
		{
			if (!shader->Uniforms)
				shader->Uniforms.reset(screen->CreateDataBuffer(POSTPROCESS_BINDINGPOINT, false));
			shader->Uniforms->SetData(step.Uniforms.Data.Size(), step.Uniforms.Data.Data());
			shader->Uniforms->BindBase();
		}

		// Set shader
		shader->Bind(NOQUEUE);

		// Draw the screen quad
		GLRenderer->RenderScreenQuad();

		// Advance to next PP texture if our output was sent there
		if (step.Output.Type == PPTextureType::NextPipelineTexture)
			NextTexture();
	}

	glViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height);

	FGLDebug::PopGroup();
}