Rendering scene into the stencil buffer, but the contents to be indepented from camera position and orientation

I want to make a scene with portals and my apprach is the following:
(my problem involves only the view from the entry portal)

1) i am drawing the scene but not the portal. (without writing into the stencil buffer at all)
2) then draw the portal while also writing to the stencil buffer a value of 2.
3) drawing the scene again on the portal where the stencil has a value of 2. (like step 1 but with the view matrix set to point to another place e.g. in front of the exit portal

The portal also updates on every movement of the light that happens at the original scene, since i have it rendered again isnide the portal

**The problem that i cant seem to solve is how can i have the contents of the portal (the view) , be in a fixed position regardless of where i point my camera and when i move left and right. **

I want to get the same view through the portal regardless if
i point my camera to the left or right (mouse movement) .
And also regardless of pressing the A or D button (the rendered scene inside the portal translates along with the camera, im assuming its because the portal is rendered with the dynamic camera
viewMatrix)

The gif below shows everything i dont want to happen. I want the exact oposite. I want regardless of the camera movement, the view from the portal to be fixed .

My code looks like this : ( viewMatrix and projectionMatrix belong to the camera)

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, W_WIDTH, W_HEIGHT);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT| GL_STENCIL_BUFFER_BIT );

**glUseProgram(shaderProgram);**

//shadows

glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, &**viewMatrix**[0][0]);
glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, &**projectionMatrix**[0][0]);
uploadLight(*light);


glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, depthTexture);
glUniform1i(depthMapSampler, 0);

mat4 light_vp = light->lightVP();
glUniformMatrix4fv(lightVPLocation, 1, GL_FALSE, &light_vp[0][0]);

//DRAWING SCENE WITH DYNAMIC CAMERA VIEW

glStencilMask(0x00);
mat4 scale = glm::scale(mat4(), vec3(0.2f, 0.2f, 0.2f));
mat4 model_matrix3 = translate(mat4(), light->lightPosition_worldspace) * scale;
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &model_matrix3[0][0]);
uploadMaterial(polishedSilver);
glUniform1i(useTextureLocation, 0);
model3->bind();
model3->draw();

glStencilMask(0x00);
glDisable(GL_DEPTH_TEST);
uploadMaterial(turquoise);
glUniform1i(useTextureLocation, 0);
float size = 15.0f;
mat4 planemodel = glm::translate(mat4(), vec3(size / 2, 0, size / 2)) *
	glm::scale(mat4(), vec3(size, 1, size));
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &planemodel[0][0]);
plane1->bind();
plane1->draw();

glStencilMask(0x00);// disable writing to the stencil buffer
uploadMaterial(turquoise);
glEnable(GL_DEPTH_TEST);
glUniform1i(useTextureLocation, 0);
planemodel = glm::translate(mat4(), vec3(size, size / 3.4f, size / 2)) *
	glm::rotate(mat4(), 3.14f / 2.0f, vec3(0, 0, 1)) * glm::scale(mat4(), vec3(size / 1.7f, 1, size));
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &planemodel[0][0]);
plane2->bind();
plane2->draw();

glStencilMask(0x00);
uploadMaterial(polishedSilver);
glUniform1i(useTextureLocation, 0);
mat4 spheremodel = glm::translate(mat4(), vec3(size / 1.5f, size / 10, size / 3))*
	glm::scale(mat4(), vec3(0.2f, 0.2f, 0.2f));;
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &spheremodel[0][0]);
model2->bind();
model2->draw();

//DRAWING THE PORTAL AND SETTING THE STENCIL BUFFER

mat4 planemodel2 = glm::translate(mat4(), vec3(size / 2.6f, size / 6, size / 1.2)) *
	glm::rotate(mat4(), 3.14f / 2.0f, vec3(0, 0, 1)) *
	glm::scale(mat4(), vec3(size / 5, 1, size / 4));


glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilFunc(GL_ALWAYS, 2, 0xFF);
glStencilMask(0xFF);
glDisable(GL_DEPTH_TEST);
    **glUseProgram(stencp);**

    glUniformMatrix4fv(viewMatrixLocation2, 1, GL_FALSE, &**viewMatrix**[0][0]);
glUniformMatrix4fv(projectionMatrixLocation2, 1, GL_FALSE, &**projectionMatrix**[0][0]);


uploadMaterial(turquoise);
glUniform1i(useTextureLocation, 0);
planemodel2 *= glm::scale(mat4(), vec3(0.9f, 0.9f, 0.9f));
glUniformMatrix4fv(modelMatrixLocation2, 1, GL_FALSE, &planemodel2[0][0]);
plane4->bind();
plane4->draw();

//REDRAW THE SCENE WHERE STENCIL=2, WITH FIXED CAMERA VIEW

mat4 VM2 = lookAt(
	vec3(5, 5, 5),
	vec3(6, 3, 5) + vec3(1, 0, 0),
	vec3(0, 1, 0)
);

glStencilFunc(GL_EQUAL, 2, 0xFF);
glStencilMask(0x00);
glDisable(GL_DEPTH_TEST);
**glUseProgram(shaderProgram);**

glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, &**VM2**[0][0]);
glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, &**projectionMatrix**[0][0]);

uploadLight(*light);


glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, depthTexture);
glUniform1i(depthMapSampler, 0);

light_vp = light->lightVP();
glUniformMatrix4fv(lightVPLocation, 1, GL_FALSE, &light_vp[0][0]);


glStencilFunc(GL_EQUAL, 2, 0xFF);
glStencilMask(0x00);
glDisable(GL_DEPTH_TEST);
model_matrix2 = translate(mat4(), vec3(-3.0, 1.0, -3.0));


scale = glm::scale(mat4(), vec3(0.2f, 0.2f, 0.2f));
model_matrix3 = translate(mat4(), light->lightPosition_worldspace) * scale;
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &model_matrix3[0][0]);
uploadMaterial(polishedSilver);
glUniform1i(useTextureLocation, 0);

model3->bind();
model3->draw();


glStencilFunc(GL_EQUAL, 2, 0xFF);
glStencilMask(0x00);
glDisable(GL_DEPTH_TEST);
uploadMaterial(turquoise);
glUniform1i(useTextureLocation, 0);
size = 15.0f;
planemodel = glm::translate(mat4(), vec3(size / 2, 0, size / 2)) *
	glm::scale(mat4(), vec3(size, 1, size));
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &planemodel[0][0]);
plane1->bind();
plane1->draw();

glStencilFunc(GL_EQUAL, 2, 0xFF);
glStencilMask(0x00);
glDisable(GL_DEPTH_TEST);
uploadMaterial(turquoise);
glUniform1i(useTextureLocation, 0);
planemodel = glm::translate(mat4(), vec3(size, size / 3.4f, size / 2)) *
	glm::rotate(mat4(), 3.14f / 2.0f, vec3(0, 0, 1)) * glm::scale(mat4(), vec3(size / 1.7f, 1, size));
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &planemodel[0][0]);
plane2->bind();
plane2->draw();

/*vec3 direction(
	cos(camera->verticalAngle)* sin(camera->horizontalAngle),
	sin(camera->verticalAngle),
	cos(camera->verticalAngle)* cos(camera->horizontalAngle)
);*/
//vec3 new_z = normalize(cross(direction, vec3(0, 1, 0)));

glStencilFunc(GL_EQUAL, 2, 0xFF);
glStencilMask(0x00);
glEnable(GL_DEPTH_TEST);
uploadMaterial(polishedSilver);
glUniform1i(useTextureLocation, 0);
spheremodel = 
	glm::translate(mat4(), vec3(size / 1.5f, size / 10, size / 3)) *
	glm::scale(mat4(), vec3(0.2f, 0.2f, 0.2f));
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &spheremodel[0][0]);
model2->bind();
model2->draw();

glStencilMask(0xFF);
glStencilFunc(GL_ALWAYS, 0, 0xFF);
glEnable(GL_DEPTH_TEST);

It’s not entirely clear what you’re asking. But the usual behaviour is to have the camera position and orientation be the same relative to both portals. IOW, if the viewpoint is 10m in front and 2m to the left of the entry portal, looking at its right edge, the scene “through” the portal would be rendered with a camera 10m in front and 2m to the left of the exit portal, looking at its right edge.

So you’d take a transformation (rotation and translation) which transforms the vertices of the entrance portal to the vertices of the entrance portal, and apply the inverse transformation to the camera.

Fixed in what space?

If I’m standing in front of a door, and I move two feet to the left, then from my perspective, the door is now two feet to the right of me. The door moved, from my perspective.

The camera is rendering a perspective that has changed. It is only natural that the portal has also moved relative to the new perspective. A portal that doesn’t move when you move the camera just doesn’t make sense.

Unless the portal is moving in world space (ie, moving relative to the world), there is no problem here.

So render a static texture on the “portal” polygon and call it done.

It’s not really a portal in that case, but that sounds like what you’re asking for.

@Dark_Photon I still want it to be a realistic portal. So there is no apparent way to fix it? Not a inverse matrix (i tried many) of some sort that will make the view through the portal not react to the camera movement? I tried to be specific and appreciate your replies

A realistic portal would react to the camera movement. It should be like looking through a doorway. If you stand to the left of the entrance portal, you see what’s to the right of the exit portal, and vice versa.

That’s achieved by rendering each scene with the same transformation relative to each portal. So that if you rendered each portal’s boundaries they would exactly overlap on screen.

1 Like

Hi Phant0m,

I’m not understanding the behavior you want either.

To help clarify could you:

  1. Use concrete examples of how this portal should behave. For instance…
    a. what happens when portal polygon moves left/right in the main perspective view? what do you see “in the portal polygon”?
    b. do you see the exact same picture in the portal as before, but distorted to fit the new oriented+projected shape of portal polygon?
    c. How is what you see different from just texturing the portal polygon with a static texture?
    d. etc.
  2. Don’t say “fixed” or “not moving”. If you’ve been doing graphics much, you know those have absolutely no meaning alone. Say “fixed in coordinate frame X”.
  3. When folks take a stab at understanding your behavior, don’t just reply: no, that’s not it. Say: it’s like that in aspect <A>, but not like that in aspect <B>. The difference is <C>.

Once you precisely convey they behavior you want, the math should be fairly straightforward.

1 Like

It seems to me that what you want isn’t a “portal” at all; it’s a television screen. You have a camera that is on the “other end” of the portal, and the view from that camera is captured and sent to some object at one end.

A portal is supposed to be like creating a window to a different place. It will show a different part of that other place depending on where you’re standing next to it. You don’t see just one view of the outside world through a window; you can stand in different places relative to the window and see different parts of the outside.

To me, this is the definition of a “realistic portal”.

A TV screen does not change perspective relative to where you view it from (LCD view angles aside). It is no more a “realistic portal” than a security camera.

The portal code from your example image isn’t correct, but it isn’t wrong because you see different parts of the other end of the portal from different locations. It’s wrong because it’s not showing the different parts correctly. Specifically, the problem appears to be that the scene behind the portal is drawn at a fixed orientation relative to the real camera.

The way the game Portal handles portals is pretty simple. It draws the main scene with depth. It then punches a hole in that scene using the stencil. Then, it takes the entire world, transforms it around such that the other end of the output portal and the viewed portal line up (in position and orientation), and it renders everything again (using the stencil to cull things outside of the hole).

And it repeats this process if the portals are looking at each other.

What you’re doing sounds similar, but you seem to be incorrectly transforming the world.

But if what you really want is just a TV screen, then you need to make a TV screen. You render the “TV camera” scene at the other end of the “portal” to a texture, then apply that texture to your end.

1 Like