• Advertisement
Sign in to follow this  

OpenGL Dual Paraboloid Mapping questions

This topic is 3515 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, Now that SH lighting is finally working, it's time for some optimizations. One of the things I had in mind, is to use Dual Paraboloid Maps instead of cubemaps. I want to use them for reflections in the world, and downscale them to tiny probes for realtime ambient lighting with spherical harmonics. DPM, The good - Only 2 passes instead of 6 DPM, The Bad - Less accurate, and/or high tesselation of world mesh required - Sampling a pixel with a given normal is more work (I think) Since the reflections and ambient lighting don't have to be 100% correct, the less accurate results don't have to be a huge problem. But... when I tried making my first DPM test program (with the help of Jason Zinks article on gamedev), I saw relative much code was required to obtain a pixel from the DPM, including a matrix multiplication:
	float3 E = normalize( cameraVec);		// normalize input per-vertex eye vector
	float3 R = reflect( E, normal );		// calculate the reflection vector
	
	R = mul( (float3x3)ParaboloidBasis, R );	// transform reflection vector to the maps basis
	
	// calculate the forward paraboloid map texture coordinates	
	float2 front;
	front.x = (R.x / (2*(1 + R.z))) + 0.5;
	front.y = 1-((R.y / (2*(1 + R.z))) + 0.5);
	
	// calculate the backward paraboloid map texture coordinates
	float2 back;
	back.x = (R.x / (2*(1 - R.z))) + 0.5;
	back.y = 1-((R.y / (2*(1 - R.z))) + 0.5);

	float3 forward  = tex2D( frontMap, front );	// sample the front paraboloid map
	float3 backward = tex2D( backMap , back );	// sample the back paraboloid map

	oColor.rgb = max(forward, backward);		// output the max of the two maps
This is not a problem for 1 sample, but I'll have to take 512 samples in the shader for up to 10 pixels per frame. The upper part of the code only has to be done once, but how about the matrix multiply and the code below? I have multiple DPM probes on different locations. Can they all do with the same matrix? If not, I'll have pass 10 or maybe 20 different matrices to the shader somehow! (10 or 20 cubeMaps or DPM's are stored in 1 3D texture). Speaking of that matrix, I haven't managed to find the right matrix yet. I thought the modelView matrix should be used (the one used when generating the DPM), but that didn't work. BTW, I'm using Cg shaders & OpenGL. Another question. The ATI ambient demo showed how to speed up by rendering multiple cubeMaps at the same time with the help of geometry shaders. I assume I can do the same trick for DPM's? I haven't used geometry shaders so far... Greetings, Rick

Share this post


Link to post
Share on other sites
Advertisement
Hello, maybe I can help out here. The matrix needs to be the view matrix that is used in the generation of the paraboloid maps. This shouldn't include any object transformations - the matrix multiplication is changing the reflection vector from world space to view space.

With that in mind, the calculation of the reflection vector and the subsequent transformation could be moved to the vertex shader, but then you will be relying on the interpolation between vertices to produce a good reflection vector. This is not really a good solution because it is even more dependant on the tessellation of the scene!

Try using the effect in its current state and see what the performance is like. I'm not entirely clear about what you are doing with it, but you may be able to squeeze out some performance elsewhere to make it fast enough. If you have any more trouble post about it here - I'm sure we can work out any kinks!

Share this post


Link to post
Share on other sites
One thing that might not be obvious: You can just assume all of your lights are oriented in an axis-aligned fashion. (eg, they're all just facing down Z, or whatever). If you do that, you don't need the matrix multiply at all, you can replace it with a simple translation by the position of the light (or probe in this case). If you have a really good reason to orient the paraboloid splitting plane in some other way, then this doesn't work. But in most cases, the decision for how to orient the two halves is fairly arbitrary anyway. This is what we do for our dual paraboloid shadow maps.

Share this post


Link to post
Share on other sites
Hey, Jason Zink! I've seen your article alot last weeks. Strange indeed that there aren't much other articles/demo's about this stuff, besides old OpenGL demo's without shaders (or assembler shaders).

To clearify things, I'm using my reflection maps (either cubeMaps or DPM's) for reflections on the world geometry & objects. Just, by simply rendering the surrounding environment at a certain point. I was planning to experiment with a grid of 9 or 20 probes (depending on system speed) in front of the camera. The camera shoots 9 or 20 rays into the directions in front of the camera. Where the rays collide, a reflection probe will be placed. Another probe is placed inside the camera itself (for the reflection on nearby objects / player itself). The probes don't have any rotation. 2 maps are just rendered into +z and -z direction. (So maybe I can simplify this, like Osmanb said).

In the end, depending on the pixel screen position, it will blend its reflection between 2 or 3, maybe even 4 probes. For example, a pixel topleft will pick the probe that was generated with the ray shooting to the topleft corner of the view frustum. I don't know if it works, but the plan is to make this grid refresh every frame == realtime & dynamic reflections.

Because I need to render 9 or even 20 + 1 probes, Dual Paraboloid Maps can save some serieus work. Cubemap = 21 * 6 = 126 passes, while DPM = 21 * 2 = "only" 42 passes. The corner probes might not be refreshed every frame, but you can see the point of using DPM's here.


However, the first problems start here. In the final reflection shader, I need access to all 9 or 20 cubeMaps/DPM's. Therefore I place each face in 1 big 3D texture. That saves me switching and sorting on textures, plus I can blend between all maps. But if the matrix is different for each DPM, I also need access to 9 or 20 matrices. Easily accessing them is a problem, unless I can do something with an array. And is uploading 20 matrix parameters not a problem?

I wondered though, like Osmanb said, is that matrix transformation nescesary? My probes ALWAYS only point into +z and -z direction. Maybe I can replace the matrix operation with something more simple... I don't need any matrices for cubeMaps neither, the pixel world normal is enough.


The second part is SH lighting. The same probes used for the reflections are downscaled to tiny maps (8x8 for cubeMaps, 16x16 if they are DPM's). I'll have to downscale less maps, which is good. But the SH shaders have to sample ALOT of pixels from these maps. To illustrate, the shader that produces SH coefficients has to loop through all reflection map pixels. That means 8x8x6 = 386 texCUBE operations for a cubeMap, or 16x16x2=512 DPM pixel-fetch operations. In the last case I need to sample from 2 textures instead of 1, and test somehow if the pixel is not outside the sphere (only 70% from the DPM texture is actually used). I have 9/20+1 probes, that also means I need to update 9/20+1 SH coefficients as fast as possible with that shader.

I'm a little bit afraid that all the speed I win with saving 4 passes per probe, is not going to compensate the extra work required in the heavy SH shaders. I can simplify the original DPM pixel shader code... And if I only win a little performance, then I might choose for cubeMaps after all, since they don't require the world to be highly tesselatedUnless

[edit]
Forget about the SH part. I'll have to loop through ALL pixels, so I don't need any code to convert a specific normal to texture coordinates, neither I'll have to read 2 textures and choose between 1 of them. The only additional cost I'll get is to determine if the pixel is in- or outside the sphere. I can do that with the alpha channel of the dual paraboloid textures.

Nevertheless, I still wonder if there are faster ways to access the DPM's, and if I can skip the matrix operation or at least use the same matrices for all DPM's.

Thanks guys,
Rick

[Edited by - spek on July 3, 2008 7:05:06 AM]

Share this post


Link to post
Share on other sites
This all sounds pretty cool. We have a fairly solid DPM implementation for our shadows here, and we've talked about using it to do other things (if we ever get time). It's not directly applicable, but you might find something useful in:

http://osman.brian.googlepages.com/dpsm.pdf

You can definitely skip the matrix multiply, assuming that you're already in world space. And you might be able to avoid most of the tesselation stuff, depending on what exactly you're doing (and how your scene). Reflections are usually pretty distorted anyway, so when you're rendering the DPMs, it probably doesn't matter too much if they're somewhat distorted in the maps. When you're rendering the scene (which is equivalent to the lighting pass in the above paper), you can do the DP transformation in the pixel shader, rather than the vertex shader. That completely eliminates any further distortion, and I suspect the results will be good enough (in most cases).

Share this post


Link to post
Share on other sites
That was the type of answer I was hoping for. Yep, the reflections don't have to be superb, I render the maps on a low resolution anyway 128x128. I downscale it to 64x64, 32x32 and 16x16 (for the ambient lighting). the passes in between are blurred as well. Most of the stuff in my world only vagely reflects, therefore a low-res blurred map is fine.

Everything is in world-space indeed. When I render a probe, I basically do this:
1.- Apply field of View = 90 (is this nescesary?), screen ratio 1.0,
near = 0.01, far = x
2.- put camera on probe world position
3.- point towards +z, or -z
4.- Render the world with dual paraboloid transformation

I've checked both maps. They seem to be ok, well, almost. Some of the polygons that are not completely inside the hemisphere, seem to be missing...


Before I try all the cool stuff from my previous post, I first try to do reflections on a simply cube. So far, they are not correct yet. I can see the maps on it, but artifacts (lines) are on the cube as well, and the reflections are for the wrong directions. I guess this has to do with the matrix. When I use the modelview matrix, I get very wrong results. The cube just has 1 color (from 1 pixel in the DPM I suppose). If I don't do the matrix operation at all, I get these artifacts, and reflections on the wrong sides. It's also as if the camera was zoomed in alot (texture appears very big on the cube).

I'm using the code I posted earlier, but without the "mul( matrix, R )" part.

Greetings and thanks for helping,
Rick

Share this post


Link to post
Share on other sites
I think Osmanb is correct - you don't need to do a full matrix multiply if the vector is already in world space AND you enforce the rule that the paraboloids are oriented along the +/- z axes. However, you will need to ensure that the vector is generated from two world space points. (EDIT: I would really like to see your shader code before making this judgement - something isn't adding up here...)

From looking at the code that you posted, it seems like some of the vector creation is mixed and matched between the original generation and accessing shaders that I sent out with the article. Please be careful to create the vectors in the correct manner! This is a definite source of problems if they aren't created correctly. Perhaps it would help if you posted the shader code for generating the maps and then the shader code for accessing the maps - it could be a simple mistake making the whole technique seem ridiculous!!

The lighting technique that you are mentioning sounds quite complex, and would bring my GPU to its knees! Even so, that is usually how the next generation techniques get started - on hardware that doesn't do it justice... Given the fact that you are using low resolution information in the lighting 'probes', I think DPM is clearly the way to go. There isn't much advantage in using cube maps if quality isn't the top priority.

Also, I don't know if OpenGL has an equivalent or not, but I have been using D3D10 texture arrays to generate my DPM maps. It allows you to render to both targets at the same time from a single draw call (by setting the desired render target in the geometry shader for each instance created in the geometry shader). I have actually written full blown book chapter on different environment mapping techniques that describes how to do this. Hopefully it will be available soon to the community for just these types of situations!

Share this post


Link to post
Share on other sites
I'm not sure, but I think the shader code is from your HLSL example. I'm using Cg though, so I had to make a few changes. For example, switching the parameters for a "mul" operation ( mul( vector, modelView ) -> mul( modelView, vector )". Anyway, I'll post the full shaders at the end of this post. Ow, and could someone please tell me the tag again to put code in a highlighted box? I forgot it.

The ambient lighting is heavy stuff indeed, although it still runs reasonable on my GeForce 8800. The main problem is not to update too much probes per frame. If you only update 2 probes, no problemo. But the problem is that worlds ussually need an awful lot of probes, which results in big (uniform 3D) grids of probes. Fine for a small scene, but way too much for a bigger (outdoor) scene. It would take maybe 10 seconds before all probes are updated, and it takes alot of memory. A waste, since most of the probes aren't even used. Therefore I thought about shooting a fixed (small) number of probes out of the camera. Updating 8 probes can be done on 50 frames per second in my card. However, all other techniques are still disabled (SSAO, DoF, HDR, Bloom, reflections, complex scenes, ... ).

Thanks for reminding me the multiple target options. I can indeed render to two textures at the same time with OpenGL as well. I'm using an indicator ("zValue") and the Cg "skip" function to discard pixels on the wrong side. But if I understand it right, the entire surrounding scene (360 degrees) is rendered in front of the camera, and I skip ~50% of it. So if I want render both of them in 1 pass:

// fragment code
// Original
out.Color.a = 0; // Invisible pixel so far
skip( zValue ); // Return if zValue is 0(or 1, I'm not familiar yet with skip)
> proceed normally
out.Color.a = 1;

// NEW CODE
> do normal code
out.colorFront = out.colorBack = myResult;
if (zValue == 1)
{
out.colorFront.a = 0; // Hide pixel in front buffer
out.colorBack.a = 1; // Shiw pixel in back buffer
} else
{
out.colorFront.a = 1; // Show pixel in front buffer
out.colorBack.a = 0; // Hide pixel in back buffer
}

The vertex shader uses a "direction" parameter to indicate if the pixel is meant for the front or back buffer. What to do with that piece of code?


Ok, here comes the code. Currently I don't do any matrix operation in the fragment shader, so I commented it out. If I enable it, I still get wrong results though.
//------------------- DPM generation -------------------------------------

< Vertex Shader >
void main(
float4 iPos : POSITION, // World position
out float4 oPos : POSITION, // Result position
out float oZValue : TEXCOORD7,

uniform float4x4 MV = MV, // ModelView Matrix
uniform half3 cameraPos = V3_CAM_POS,
uniform half direction = F_CAM_DIRECTION // +1 or -1
)
{
// Bend vertex into the paraboloid
oPos = mul( MV, float4(iPos.xyz,1) );// transform vertex into the maps basis
oPos = oPos / oPos.w; // divide by w to normalize

oPos.z = oPos.z * direction; // set z-values to forward or backward

float L = length( oPos.xyz ); // determine the distance between (0,0,0) and the vertex
oPos = oPos / L; // divide the vertex position by the distance

oZValue = oPos.z; // remember which hemisphere the vertex is in
oPos.z = oPos.z + 1; // add the reflected vector to find the normal vector

oPos.x = oPos.x / oPos.z; // divide x coord by the new z-value
oPos.y = oPos.y / oPos.z; // divide y coord by the new z-value

const half MAX_VIEW_DIST = 500;
oPos.z = L / MAX_VIEW_DIST; // set a depth value for correct z-buffering
oPos.w = 1; // set w to 1 so there is no w divide

//---------------------------------------------------------------------
... Additional code for the lighting in the fragment shader
... shadowMap matrices, passing texcoords / normals, etc.

} // VP_Probe_Paraboloid


< Fragment Shader >
void main(
// VERTEX SHADER INPUT
...
float iZValue : TEXCOORD7,

out half4 oColor : COLOR0, // Result
)
{
oColor.a = 0;
clip( iZValue ); // Return if iZValue = 0?
// I'm not familiar with this Cg function

... Calculate lighting

// RESULT
oColor.rgb = (albedo * directDiffuse + emissive) * fog;
oColor.a = 1;

} // FP_Probe_Paraboloid

//------------------- DPM Usage ---------------------------------------------

< Vertex Shader >
void main(
float4 iPos : POSITION, // world position
float3 iNormal : NORMAL, // world normal

out float4 oPos : POSITION,
out float3 oNormal : TEXCOORD2,
out float3 oCamVec : TEXCOORD3,

uniform float4x4 MVP = MVP, // ModelViewProjection
uniform half3 cameraPos = V3_CAM_POS
)
{
oNormal.xyz = iNormal.xyz; // Pass world normal
oCamVec.xyz = iPos.xyz - cameraPos;
oPos = mul( MVP, iPos );
} // VP_DPM_Test

< Fragment Shader >
void main(
out half4 oColor : COLOR0,

float3 iNormal : TEXCOORD2,
float3 iCamVec : TEXCOORD3,

uniform sampler2D frontMap : TEXUNIT0,
uniform sampler2D backMap : TEXUNIT1,

uniform float3x3 ParaboloidBasis = TEX_MATRIX1 // modelView used when generating the probe.
// Stored in texture matrix
)
{
float3 E = normalize( iCamVec ); // normalize input per-vertex eye vector
float3 R = reflect( E, iNormal ); // calculate the reflection vector

//! R = mul( (float3x3)ParaboloidBasis, R ); // transform reflection vector to the maps basis

// calculate the forward paraboloid map texture coordinates
float2 front;
front.x = (R.x / (2*(1 + R.z))) + 0.5;
front.y = 1-((R.y / (2*(1 + R.z))) + 0.5);

// calculate the backward paraboloid map texture coordinates
float2 back;
back.x = (R.x / (2*(1 - R.z))) + 0.5;
back.y = 1-((R.y / (2*(1 - R.z))) + 0.5);

float3 forward = tex2D( frontMap, front ); // sample the front paraboloid map
float3 backward = tex2D( backMap , back ); // sample the back paraboloid map

oColor.rgb = max(forward, backward); // output the max of the two maps
oColor.a = 1;

} // FP_DPM_Test


Thanks, and good luck with your book!
Rick

Share this post


Link to post
Share on other sites
I think I see two different things in the shader code that could be changed. The first is not a source of error, but could be removed nonetheless. The divide by w after transforming to the paraboloid view space in the map generation vertex shader isn't needed. I had included it in the original article, but it isn't necesary since the matrix multiply doesn't include any projection (i.e. the w values will always be 1 without the divide anyways). So you can remove an unneeded divide from the vertex shader [grin].

The second point, and probably at least one source of error, is that the normal vector in the map accessing vertex shader is not transformed to world space. You are essentially using the object space normal vector of the geometry that is using the DPM instead of the world space vector. Here's the same operation from the original article shader code:

float3 N = normalize(mul(IN.normal, (float3x3)ModelWorld)); // find world space normal

This should at least be one step in the right direction. Try using the matrix multiply as it was originally, and then once it is working you can try to simplify and remove the instruction.

Also, I think the multiple render targets that you mention above is a little different than the texture arrays that I was talking about before (which use the geometry shader instead of the fragment shader). Even so, I think MRT should be useable as well but wouldn't really gain much performance. I would be very interested to hear if you try it out and have some success though!

Hopefully this helps get you up and running!

[Edited by - Jason Z on July 4, 2008 11:17:48 AM]

Share this post


Link to post
Share on other sites
Removing the division with position.w didn't hurt, removed that part. I don't understand the world-normal part though. All the vertex positions and normal are already in world space. Nothing is transformed, rotated or scaled (so far I'm only rendering a static world).

I tried multiplying the normal, but that didn't really work. But probably because I choose the wrong matrix. I'm not 100% sure, but "ModelView" matrix is not the same as "ModelWorld" matrix. This gives me the idea I'm using the wrong matrix for the "R = mul( dualParaboloidBasis, normal )" line as well. ModelWorld is not defined in OpenGL if I'm right, so I should make it myself. Unfortunately, I'm a disaster with matrices, so I need a helping hand on that.

But in the end I'm hoping to skip that part anyway. But yet again, I don't know how to correctly replace that part. Just commenting out the "R = mul..." line is not working. Here are some screenshots from what I have so far:
http://img364.imageshack.us/my.php?image=dpm1jh2.jpg
http://img354.imageshack.us/my.php?image=dpm2rq0.jpg

What you see on in the front/back maps is correct, although there a couple of problems. It's not exactly a nice circle. It seems that border polygons are falling away. You see the greenish polygons? These are polygons behind the ceiling/walls/floor. Normally they are culled away (therefore they have the wrong green color), but I disabled face culling. If I enable face culling, I only see the inverted world on the front map.

The cube with the reflections is not exactly correct either. That's because of skipping the matrix I suppose. If I enable the matrix operation, eventually with transforming the normal, the entire cube has only 1 color.
[edit]
I tried some simple code:

float3 E = normalize( iCamVec ); // normalize input per-vertex eye vector
float3 R = reflect( E, iNormal ); // calculate the reflection vector
if (R.z > 0)
{
float2 tx;
tx.x = 1-((R.x+1)*0.5); // maybe texture is swapped
tx.y = (R.y+1)*0.5;
oColor.rgb = tex2D( frontMap, tx );
} else
{
float2 tx;
tx.x = 1-((R.x+1)*0.5);
tx.y = (R.y+1)*0.5;
oColor.rgb = tex2D( backMap , tx );
}

This seems to work, without any matrices. I still see some wrong parts in the reflection though. That is because the circle is not filled entirely (see the 2 shots I posted). I don't know how to fix that. Maybe my scene is not tesselated enough? I estemate the big walls are subdivided in 1.5 x 1.5 metre parts.

Greetings,
Rick

[Edited by - spek on July 5, 2008 11:09:43 AM]

Share this post


Link to post
Share on other sites
Yes, you are correct. If the positions and normal vectors are already in world space (i.e. the only matrix in the modelview is the view matrix) then you don't need to multiply the normal.

After looking at the images that you posted and your comments about not filling in the circle - these symptoms are exactly what you will see if the scene isn't tessellated enough. Try rendering the paraboloid maps in wireframe and see what comes out. If there are any triangles that appear larger than ~10 pixels on any one side then it is either not tessellated finely enough or you are somehow rendering geometry that shouldn't be on that side of the paraboloid.

I think you are pretty close - just a few more small changes and you should be up and rendering...

EDIT: I am no expert in OpenGL, but I have used it a few times. The 'ModelView' matrix is indeed the combination of the world and view matrices from D3D. When setting the ModelView matrix, you set the identity first, then the view matrix on the matrix stack. Next you push on the model's world matrix, which gets pre-multiplied to the view matrix. The end result is a matrix that transforms the object space geometry to world space to view space all in one step.

Share this post


Link to post
Share on other sites
I'm happy to hear that I'm close. And the simplified reflection code works without costing too much energy. The only 2 problems are the missing polygons and the place where the 2 maps connect. The seam is not much noticable on curved objects, but a wall facing +x for example, will have a seam right in the middle (when facing straight forward). Maybe higher tesselation, higher render resolution (now I use 128x128) and/or blurring helps.

As for the tesselation, that will be the first thing to try. Hopefully it fixes the problem. However, no matter how much I increase the detail, very nearby polygons will always be relative big on the screen. I'm afraid there will always be some problems around the seams of the maps.


Another thing to consider is the tesselation itself. Maybe the bigger polycount itself isn't that much of a problem. I mean, a 10x10x4 meter chamber could be done with 6 quads. If I want 0.5 x 0.5 meter quads, I get 2880 polygons. Sounds much comparing to 12 polygons, but a few characters will have that polycount too. However, DPM is not the only thing that needs to be done. A lot of other passes need to be done as well. Maybe the lighting can profit from this as well (now I'm using cubemaps for pointlights), but ultimately, I have a low-res and high-res model. High-res for DPM passes, low-res for the rest.

My philosofy is that the map designer (if I ever get one crazy enough to share my hobby :) ) shouldn't be bothered with technical adjustments while mapping. Just sub-dividing the entire mesh into smaller bits is not a good option. Huge polygons stay huge, and already small polygons will get unnessarily much detail.

I could make a tool that makes a high-detail version of the original map. I don't know if its difficult to make such a thing. Another option might be tesselation in the vertex shader...? I never done it, but it's possible to increase the polycount realtime on the GPU right? Ifso, would that be a good thing to do, or would that cost way too much energy? In the end I'm using DPM to gain speed over cubeMaps, not to loose of course.

Osman's paper showed another way to do DPM with low-res models. It's not very clear to me how it works though. But maybe that's because I didn't have time to read it thoroughly so far.

[edit]
Another thing I'm thinking about right now. We save speed by only doing 2 passes instead of 6. But we also need bigger resolutions to get the same quality as a cubemap. In fact, I need even more pixels. For example, a cubeMap with 64x64 pixels has 24.576 pixels in total. I need at least a 128x128 DPM to match quality. 128x128x2 = 32.768 pixels. The actual quality is still less, since ~70% of the map is used -> 22.938 pixels. Of course I save speed with less pushing geometry to the hardware and less texture/shader switches. But the higher tesselation also has its costs. In fact, rendering a low-res scene (simple cube chamber with 12 polygons for example) 6 times (6x12 = 72) could still be (far) less polygons than render a high-tesselated scene twice.

So does DPM really a give a speed gain over cubeMaps, when trying to have the same quality as a cubeMap? I know, the best way to test this is by trying both. But maybe someone else also did some tests, and/or can explain where the real advantage is?

Thanks Jason & Osmanb,
Rick

[Edited by - spek on July 5, 2008 1:43:32 PM]

Share this post


Link to post
Share on other sites
I'm just glad to see people experimenting with DPM. We were sort of forced into using it due a combination of requirements, mostly the need to support several shadowing point lights simultaneously, and now we've grown to like it.

The technique from the paper I posted (doing the paraboloid projection in the pixel shader) just fixes the distortion during the receiver pass (which would be like your lighting pass). It doesn't help with the shadow-render pass (which would be equivalent to your pass that renders the reflecting environment into the DPMs).

However, we also did do some experiments with automatic hardware tessellation ... I'm not sure if that's actually available on most PCs or not. You still have the issues you've mentioned, where you only want to tessellate things that need it.

Another possibility, although we haven't tried this yet: There was a paper at the last I3D that talked about a way of removing the need for tessellation even during the initial pass.

http://portal.acm.org/citation.cfm?id=1342250.1342267&coll=GUIDE&dl=&type=series&idx=SERIES749%E2%88%82=series&WantType=Proceedings&title=I3D

I haven't read the whole thing, but I'm pretty sure the technique is basically two things:

1) For each triangle in the model, you can compute a larger polygon (which might even be two triangles) whose paraboloid projection is guaranteed to bound the correct paraboloid projection of the actual triangle. Basically, for large triangles, you artifically inflate them, so that when they get linearly rasterized into the DPMs, the resulting area covers the correct, curved, version of the original triangle. This might require geometry shaders (or an expensive pre-pass on the CPU).

2) In your pixel shader, during that initial pass, you do the math to figure out if the pixel you're rendering is actually inside the paraboloid projection of the triangle. If not, you kill it with something like 'clip'. In addition, your pixel shader outputs the correct (paraboloid warped) depth value, rather than the (incorrect, linearly interpolated) one from the rasterization.

Those two things are both fairly expensive, so it's not clear if the technique is really feasible, especially in your case where you want to be rendering several DPMs. But on the other hand, they produce effectively PERFECT paraboloid views of your scene, with no tessellation requirement at all.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By LifeArtist
      Good Evening,
      I want to make a 2D game which involves displaying some debug information. Especially for collision, enemy sights and so on ...
      First of I was thinking about all those shapes which I need will need for debugging purposes: circles, rectangles, lines, polygons.
      I am really stucked right now because of the fundamental question:
      Where do I store my vertices positions for each line (object)? Currently I am not using a model matrix because I am using orthographic projection and set the final position within the VBO. That means that if I add a new line I would have to expand the "points" array and re-upload (recall glBufferData) it every time. The other method would be to use a model matrix and a fixed vbo for a line but it would be also messy to exactly create a line from (0,0) to (100,20) calculating the rotation and scale to make it fit.
      If I proceed with option 1 "updating the array each frame" I was thinking of having 4 draw calls every frame for the lines vao, polygons vao and so on. 
      In addition to that I am planning to use some sort of ECS based architecture. So the other question would be:
      Should I treat those debug objects as entities/components?
      For me it would make sense to treat them as entities but that's creates a new issue with the previous array approach because it would have for example a transform and render component. A special render component for debug objects (no texture etc) ... For me the transform component is also just a matrix but how would I then define a line?
      Treating them as components would'nt be a good idea in my eyes because then I would always need an entity. Well entity is just an id !? So maybe its a component?
      Regards,
      LifeArtist
    • By QQemka
      Hello. I am coding a small thingy in my spare time. All i want to achieve is to load a heightmap (as the lowest possible walking terrain), some static meshes (elements of the environment) and a dynamic character (meaning i can move, collide with heightmap/static meshes and hold a varying item in a hand ). Got a bunch of questions, or rather problems i can't find solution to myself. Nearly all are deal with graphics/gpu, not the coding part. My c++ is on high enough level.
      Let's go:
      Heightmap - i obviously want it to be textured, size is hardcoded to 256x256 squares. I can't have one huge texture stretched over entire terrain cause every pixel would be enormous. Thats why i decided to use 2 specified textures. First will be a tileset consisting of 16 square tiles (u v range from 0 to 0.25 for first tile and so on) and second a 256x256 buffer with 0-15 value representing index of the tile from tileset for every heigtmap square. Problem is, how do i blend the edges nicely and make some computationally cheap changes so its not obvious there are only 16 tiles? Is it possible to generate such terrain with some existing program?
      Collisions - i want to use bounding sphere and aabb. But should i store them for a model or entity instance? Meaning i have 20 same trees spawned using the same tree model, but every entity got its own transformation (position, scale etc). Storing collision component per instance grats faster access + is precalculated and transformed (takes additional memory, but who cares?), so i stick with this, right? What should i do if object is dynamically rotated? The aabb is no longer aligned and calculating per vertex min/max everytime object rotates/scales is pretty expensive, right?
      Drawing aabb - problem similar to above (storing aabb data per instance or model). This time in my opinion per model is enough since every instance also does not have own vertex buffer but uses the shared one (so 20 trees share reference to one tree model). So rendering aabb is about taking the model's aabb, transforming with instance matrix and voila. What about aabb vertex buffer (this is more of a cosmetic question, just curious, bumped onto it in time of writing this). Is it better to make it as 8 points and index buffer (12 lines), or only 2 vertices with min/max x/y/z and having the shaders dynamically generate 6 other vertices and draw the box? Or maybe there should be just ONE 1x1x1 cube box template moved/scaled per entity?
      What if one model got a diffuse texture and a normal map, and other has only diffuse? Should i pass some bool flag to shader with that info, or just assume that my game supports only diffuse maps without fancy stuff?
      There were several more but i forgot/solved them at time of writing
      Thanks in advance
    • By RenanRR
      Hi All,
      I'm reading the tutorials from learnOpengl site (nice site) and I'm having a question on the camera (https://learnopengl.com/Getting-started/Camera).
      I always saw the camera being manipulated with the lookat, but in tutorial I saw the camera being changed through the MVP arrays, which do not seem to be camera, but rather the scene that changes:
      Vertex Shader:
      #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec2 aTexCoord; out vec2 TexCoord; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = projection * view * model * vec4(aPos, 1.0f); TexCoord = vec2(aTexCoord.x, aTexCoord.y); } then, the matrix manipulated:
      ..... glm::mat4 projection = glm::perspective(glm::radians(fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); ourShader.setMat4("projection", projection); .... glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); ourShader.setMat4("view", view); .... model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f)); ourShader.setMat4("model", model);  
      So, some doubts:
      - Why use it like that?
      - Is it okay to manipulate the camera that way?
      -in this way, are not the vertex's positions that changes instead of the camera?
      - I need to pass MVP to all shaders of object in my scenes ?
       
      What it seems, is that the camera stands still and the scenery that changes...
      it's right?
       
       
      Thank you
       
    • By dpadam450
      Sampling a floating point texture where the alpha channel holds 4-bytes of packed data into the float. I don't know how to cast the raw memory to treat it as an integer so I can perform bit-shifting operations.

      int rgbValue = int(textureSample.w);//4 bytes of data packed as color
      // algorithm might not be correct and endianness might need switching.
      vec3 extractedData = vec3(  rgbValue & 0xFF000000,  (rgbValue << 8) & 0xFF000000, (rgbValue << 16) & 0xFF000000);
      extractedData /= 255.0f;
    • By Devashish Khandelwal
      While writing a simple renderer using OpenGL, I faced an issue with the glGetUniformLocation function. For some reason, the location is coming to be -1.
      Anyone has any idea .. what should I do?
  • Advertisement