Skip to main content
gamedev.net
🔒 Locked 🎯 Unreal

Render to texture problem

Started by Hnefi Mar 24, 2006 at 12:37 PM 8 replies 5.4k views
Original Post
Hnefi
Hnefi
Hiyall. I'm trying to get a scene rendered to a texture and then display that texture onto a quad (which I can then play with as I like). To help me, I used som documentation from nVidia (http://developer.nvidia.com/attach/6725). Unfortunately, it's a presentation and not always as precise as I might have needed. So all I manage to produce is an untextured quad. Since the documentation doesn't go into great detail and SGI doesn't seem to know how to document properly at all (the extension registry is barely useable), I have great difficulty in debugging my program. So maybe you guys can help me. Any hints, tips, suggestions and, of course, complete solutions are very much appreciated. This is my extension initialization code: Extensions.h:
#ifndef EXT_HEADER
#define EXT_HEADER
#include <windows.h>
#include <gl/gl.h>
#include <gl/glext.h>
#include <gl/wglext.h>
enum {XT_NONE = 0, XT_MULTITEX = 1, WGL_RENDERTEX = 2, WGL_PBUF = 4, WGL_PIXFORMAT = 8};

//multitex
extern PFNGLMULTITEXCOORD2DARBPROC glMultiTexCoord2d;
extern PFNGLACTIVETEXTUREARBPROC glActiveTexture;

//rendertex
extern PFNWGLBINDTEXIMAGEARBPROC wglBindTexImage;
extern PFNWGLRELEASETEXIMAGEARBPROC wglReleaseTexImage;
extern PFNWGLSETPBUFFERATTRIBARBPROC wglSetPbufferAttrib;

//pbuffer
extern PFNWGLCREATEPBUFFERARBPROC wglCreatePbuffer;
extern PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDC;
extern PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDC;
extern PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbuffer;
extern PFNWGLQUERYPBUFFERARBPROC wglQueryPbuffer;

//pixel format
extern PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribiv;
extern PFNWGLGETPIXELFORMATATTRIBFVARBPROC wglGetPixelFormatAttribfv;
extern PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat;

struct Ext{
	static GLuint rendertexture;
	static HDC pbufdc;
	static HGLRC pbufglrc;
	static HPBUFFERARB pbuffer;
	
	static unsigned int active;
	static void InitExtensions();
	static void DeInitExtensions();

private:
	static void CheckRequired();
};
#endif


Extensions.cpp, where I try to actually create the pbuffer and its "texture". This is probably where the error is, somewhere.
#include "Extensions.h"
#include "Error.h"
#include <string>
#include <windows.h>

unsigned int Ext::active;

//multitex
PFNGLMULTITEXCOORD2DARBPROC glMultiTexCoord2d;
PFNGLACTIVETEXTUREARBPROC glActiveTexture;

//rendertex
PFNWGLBINDTEXIMAGEARBPROC wglBindTexImage;
PFNWGLRELEASETEXIMAGEARBPROC wglReleaseTexImage;
PFNWGLSETPBUFFERATTRIBARBPROC wglSetPbufferAttrib;

//pbuffer
PFNWGLCREATEPBUFFERARBPROC wglCreatePbuffer;
PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDC;
PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDC;
PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbuffer;
PFNWGLQUERYPBUFFERARBPROC wglQueryPbuffer;

//pixel format
PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribiv;
PFNWGLGETPIXELFORMATATTRIBFVARBPROC wglGetPixelFormatAttribfv;
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat;

GLuint Ext::rendertexture;
HDC Ext::pbufdc;
HPBUFFERARB Ext::pbuffer;
HGLRC Ext::pbufglrc;


void Ext::InitExtensions(){
	PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
	if(wglGetExtensionsStringARB == NULL){
		wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
		if(wglGetExtensionsStringARB == NULL)
			Error(0, "Hardware lacks wgl support.");
	}
	
	active = 0;
	char* extstring = (char*)glGetString(GL_EXTENSIONS);
	char* wglstring = (char*)wglGetExtensionsStringARB(wglGetCurrentDC());
	
	//extensions
	if(strstr(extstring,"ARB_multitexture ") != NULL && strstr(extstring,"GL_EXT_texture_env_combine") != NULL){ /**** not 100% accurate*/
		active = active | XT_MULTITEX;
		glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DARBPROC)wglGetProcAddress("glMultiTexCoord2dARB");
		glActiveTexture = (PFNGLACTIVETEXTUREARBPROC)wglGetProcAddress("glActiveTextureARB");
	}//if(strstr(extstring,"ARB_multitexture ") != NULL)

	//wgl extensions
	if(strstr(wglstring,"WGL_ARB_render_texture ") != NULL || strstr(wglstring," WGL_ARB_render_texture") != NULL){
		active = active | WGL_RENDERTEX;
		wglBindTexImage = (PFNWGLBINDTEXIMAGEARBPROC)wglGetProcAddress("wglBindTexImageARB");
		wglReleaseTexImage = (PFNWGLRELEASETEXIMAGEARBPROC)wglGetProcAddress("wglReleaseTexImageARB");
		wglSetPbufferAttrib = (PFNWGLSETPBUFFERATTRIBARBPROC)wglGetProcAddress("wglSetPbufferAttribARB");
	}
	if(strstr(wglstring,"WGL_ARB_pbuffer ") != NULL || strstr(wglstring," WGL_ARB_pbuffer") != NULL){
		active = active | WGL_PBUF;
		wglCreatePbuffer = (PFNWGLCREATEPBUFFERARBPROC)wglGetProcAddress("wglCreatePbufferARB");
		wglGetPbufferDC = (PFNWGLGETPBUFFERDCARBPROC)wglGetProcAddress("wglGetPbufferDCARB");
		wglReleasePbufferDC = (PFNWGLRELEASEPBUFFERDCARBPROC)wglGetProcAddress("wglReleasePbufferDCARB");
		wglDestroyPbuffer = (PFNWGLDESTROYPBUFFERARBPROC)wglGetProcAddress("wglDestroyPbufferARB");
		wglQueryPbuffer = (PFNWGLQUERYPBUFFERARBPROC)wglGetProcAddress("wglQueryPbufferARB");
	}
	if(strstr(wglstring,"WGL_ARB_pixel_format ") != NULL || strstr(wglstring," WGL_ARB_pixel_format") != NULL){
		active = active | WGL_PIXFORMAT;
		wglGetPixelFormatAttribiv = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)wglGetProcAddress("wglGetPixelFormatAttribivARB");
		wglGetPixelFormatAttribfv = (PFNWGLGETPIXELFORMATATTRIBFVARBPROC)wglGetProcAddress("wglGetPixelFormatAttribfvARB");
		wglChoosePixelFormat = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
	}
	
	
	if(active & (WGL_PIXFORMAT | WGL_PBUF | WGL_RENDERTEX)){
		GLuint PBufPixelFormat;
		HDC hdc;
		int formats[2];
		UINT numforms;
		
		glGenTextures(1, &rendertexture);
		glBindTexture(GL_TEXTURE_2D, rendertexture);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);		
		
		hdc = wglGetCurrentDC();
		int attrib[] = {WGL_DRAW_TO_PBUFFER_ARB, 1, PFD_SUPPORT_OPENGL, 1, WGL_BIND_TO_TEXTURE_RGBA_ARB, 1, PFD_TYPE_RGBA, 1, 0};
		
		if(!wglChoosePixelFormat(hdc, attrib, NULL, 2, formats, &numforms))
			Error(0, "Failed to choose extended pixel format.");
		
		PBufPixelFormat = formats[0];
		
		int attrib2[] = {WGL_TEXTURE_FORMAT_ARB, WGL_TEXTURE_RGBA_ARB, WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_2D_ARB, WGL_MIPMAP_TEXTURE_ARB, 1, WGL_PBUFFER_LARGEST_ARB, 1, 0};
		wglCreatePbuffer(hdc, PBufPixelFormat, 640, 480, attrib2);
		
		pbufdc = wglGetPbufferDC(pbuffer);
		pbufglrc = wglCreateContext(pbufdc);
		
		//wglQueryPbuffer(pbuffer, WGL_PBUFFER_WIDTH_ARB, width);
		//wglQueryPbuffer(pbuffer, WGL_PBUFFER_HEIGHT_ARB, height);
	}
	
	delete extstring;
	delete wglstring;
	
	CheckRequired();
}//void InitExtensions()

void Ext::DeInitExtensions(){
	if(active & (WGL_PIXFORMAT | WGL_PBUF | WGL_RENDERTEX)){
		wglDeleteContext(pbufglrc);
		wglReleasePbufferDC(pbuffer, pbufdc);
		wglDestroyPbuffer(pbuffer);
	}
}//void Ext::DeInitExtensions()

void Ext::CheckRequired(){
	if(!(active & XT_MULTITEX))
		Error(0, "Hardware lacks multitexture support.");
	if(!(active & WGL_RENDERTEX))
		Error(0, "Hardware lacks rendertexture support.");
	if(!(active & WGL_PBUF))
		Error(0, "Hardware lacks pbuffer support.");
	if(!(active & WGL_PIXFORMAT))
		Error(0, "Hardware lacks wgl pixel format support.");
}//void Ext::CheckRequired()

This is my rendering function, where I try to use the rendering texture:
int DrawGLScene(GLvoid){
	wglMakeCurrent(Ext::pbufdc, Ext::pbufglrc);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	Game::Render();
	wglMakeCurrent(hDC, hRC);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_LIGHTING);
	glBindTexture(GL_TEXTURE_2D, Ext::rendertexture);
	wglBindTexImage(Ext::pbuffer, WGL_FRONT_LEFT_ARB);
	glColor4d(1, 1, 1, 1);
	glBegin(GL_QUADS);
	glTexCoord2d(0, 0);
	glVertex3d(-1, -1, -1);
	glTexCoord2d(0, 1);
	glVertex3d(-1, 1, -1);
	glTexCoord2d(1, 1);
	glVertex3d(1, 1, -1);
	glTexCoord2d(1, 0);
	glVertex3d(1, -1, -1);
	glEnd();
	wglReleaseTexImage(Ext::pbuffer, WGL_FRONT_LEFT_ARB);
	return TRUE;
}//int DrawGLScene(GLvoid){

This is my main loop (might be irrelevant, and it's pretty big but anyway...):
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){

	MSG msg;
	bool timertick;
	/*
	if(MessageBox(NULL, "Do you want to run in fullscreen mode?", "Inquiry", MB_YESNO|MB_ICONQUESTION)==IDNO)
		fullscreen=FALSE;
	else fullscreen=TRUE;*/
	fullscreen = false;
	

	if(!CreateGLWindow("Schmup v. 0.02", Game::width, Game::height, 16, fullscreen))
		return 0;

	InitProgram();
	BuildFont();

	while(!Game::done){
		if(PeekMessage(&msg,NULL, 0, 0, PM_REMOVE)){
			if(msg.message==WM_QUIT){
				Game::done=TRUE;
			}//if(msg.message==WM_QUIT){
			else{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}//else{
		}//if(PeekMessage(&msg,NULL, 0, 0, PM_REMOVE)){
		else{
			if(Game::menu.Active()){
				if(lbclick){
					Game::menu.Click(mpos);
				}//if(leftclick[1] && !leftclick[0])
					
				if(keys[VK_ESCAPE] && Game::gameon){
					keys[VK_ESCAPE] = false;
					Game::menu.Show(false);
				}//if(keys[VK_ESCAPE])
				DrawGLScene();
				Game::menu.Render();
				SwapBuffers(hDC);
			}//if(Game::menuactive)
			
			else if(Game::hangar.Active()){
				if(lbclick){
					Game::hangar.Click(mpos);
				}//if(leftclick[1] && !leftclick[0])
				if(keys[VK_ESCAPE]){
					keys[VK_ESCAPE] = false;
					Game::menu.Show(true);
				}//if(keys[VK_ESCAPE])
				DrawGLScene();
				Game::hangar.Render();
				SwapBuffers(hDC);
			}//if(Game::menuactive)
			
			else if(active && timertick){
				Game::timer->SetTime();
				timertick = false;
				if(keys[VK_ESCAPE]){
					keys[VK_ESCAPE] = false;
					Game::menu.Show(true);
				}//if(keys[VK_ESCAPE])
				else{
					if(keys[(int)'G']){
						delete Game::world->player;
						Game::world->player = new Player();
					}
					if(keys[(int)'A']){
						Game::world->player->turnleft = true;
					}
					if(keys[(int)'D']){
						Game::world->player->turnright = true;
					}
					if(keys[(int)'W']){
						Game::world->player->accelerate = true;
					}
					if(keys[(int)'S']){
						Game::world->player->decelerate = true;
					}
					if(keys[(int)'Q']){
						Game::world->player->strafeleft = true;
					}
					if(keys[(int)'E']){
						Game::world->player->straferight = true;
					}
					if(keys[VK_SPACE]){
						Game::world->player->do_fire = true;
					}
					if(keys[VK_UP]){
						Game::ZoomIn();
					}
					if(keys[VK_DOWN]){
						Game::ZoomOut();
					}
				}//else{
				Game::world->Tick();
				DrawGLScene();
				SwapBuffers(hDC);
			}//if(active){
			else if(Game::timer->GetTime() > 60000)
				timertick = true;
			lbclick = false;
		}//else{

	}//while(!done){

	Game::DeInitGame();
	KillGLWindow();
	KillFont();
	Ext::DeInitExtensions();
	return (msg.wParam);
}//int WinAPI WinMain(...){

Initextensions is called in InitProgram, which otherwise only contains miscellaneous code dealing with the game world, mouse input etc.
-------------Please rate this post if it was useful.
jon723
jon723
I didnt read through all of your code but, have you tried using glCopyTexImage to render your scene to a texture?
www.lefthandinteractive.net
Hnefi
Hnefi
glCopyTexImage is rather slow and will introduce a performance bottleneck into the program. That's why I'm trying to use a pbuffer. I only wish it would work :/
-------------Please rate this post if it was useful.
Falken42
Falken42
I'm pretty sure you have to call wglShareLists to allow the base rendering context to access the texture directly in the pbuffer context.

There's also some nice samples on how to render to a buffer (3 different variations) on CodeSampler.com that you might want to take a look at.
Aragon
Aragon
The codesampler tutorial is much much better!!!!


but when using that at my engine the textures
in the pbuffer textur are not used when
using light.. in main scene everything works
fine
Hnefi
Hnefi
Quote:
Original post by bpoint
I'm pretty sure you have to call wglShareLists to allow the base rendering context to access the texture directly in the pbuffer context.

There's also some nice samples on how to render to a buffer (3 different variations) on CodeSampler.com that you might want to take a look at.


Thank you, I'll have a look at these examples as soon as I get home.
-------------Please rate this post if it was useful.
gold
gold
Pbuffers are the old way, the hard way, the slow way to render to texture. We'd like to deprecate this.

EXT_framebuffer_object is much simpler, the new way, hopefully even the fast way. For one thing, it eliminates the need for multiple contexts, and wglShareLists, and wglMakeCurrent, and all kinds of mean, nasty things they imply.

The spec is here, but I don't recommend trying to learn from reading this spec. Does anyone have a sample handy? Maybe I need to write one up.
Hnefi
Hnefi
Gack, that's one hefty specification. But FBO's do seem to be the way to go. I'll have to take a very serious look at them.

Using pbuffers do seem to require a lot of black magic and voodoo, and I never was a good magician.
-------------Please rate this post if it was useful.
Falken42
Falken42
Quote:
Original post by gold
Pbuffers are the old way, the hard way, the slow way to render to texture. We'd like to deprecate this.

If the WGL_ARB_render_texture extention is available with pbuffers, I think you'll find the performance is about the same as FBOs. As long as you only need the color buffer, pbuffers should be easier to implement overall.

Besides, only fairly recent hardware within the last 2 years or so support FBOs. While it's sometimes nice to deprecate older methods, people with older hardware will be unable to use your application.
Niwak
Niwak
As far as I know, all cards supporting PBuffers supports FBO. It's just a matter of updating the driver. Therefore I would strongly discourage using PBuffer, the overhead of context switching management makes them a hell to use and to integrate in an engine.

FBO are pretty simple. There are a few examples available across the internet (just google them, NVidia SDK has an example of a simple FBO RTT and another of FBO shadowmap which are pretty simple) and there is an example in the spec for porting an existing application that uses glCopyTexImage to FBO.

When I ported my engine (which initially used glCopyTexImage, then PBuffer, then reverted to glCopyTexImage,...), the 2 things I forgot and were causing bugs were that you have to call glViewport after binding the frame buffer (viewport state is not per framebuffer) and that you have to set your depth test (again depth test is not per framebuffer). After that everything went very smoothly.

Vincent

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.