• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.

Chozo

Members
  • Content count

    279
  • Joined

  • Last visited

Community Reputation

176 Neutral

About Chozo

  • Rank
    Member
  1. Ok, going back to the cube now that I'm home, still using 128 as a starting value, I see the full cube geometry with a 129 in the stencil where the cube is at. A little more messing around and it seems to be related to front face culling. For example, I can remove the increment and change the decrement to work on front faces and it decrements fine. I can also change the increment to work on back faces and it never increments. But I can render the cube with whichever face culling outside of shadows and it works fine.
  2. Just going to note that when I get home and can get back to the box shadows, I'm going to try with a clear value of 128 and check the variance of values. I'm guessing stencil indices can't be negative? It's possible that may be getting in the way here.
  3. Unfortunately, I'm not in a place to test with the cube right now, but I did manage to dump the stencil buffer for my shadow volume (clearing with and checking against 0 rather than 1 here) to a text file with this: [code] const size_t s = window_width() * window_height(); boost::shared_array<unsigned char> stencil(new unsigned char[s]); glPixelStorei(GL_PACK_ALIGNMENT, 1); glReadPixels(0, 0, window_width(), window_height(), GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencil.get()); glPixelStorei(GL_PACK_ALIGNMENT, 4); std::ofstream f("stencil.txt"); for(size_t y=0; y<window_height(); ++y) { for(size_t x=0; x<window_width(); ++x) { f << static_cast<int>(stencil[(y * window_width()) + x]) << " "; } f << std::endl; } f << std::endl; f.close(); [/code] and I'm seeing values larger than 1. In fact I see all the way up to 9. It really is starting to seem like something is wrong with my decrementing.
  4. Sorry, removing some posts that came about as a result of me making some mistakes in testing. Both back and front face culling seem to be working.
  5. Sorry, I did that cube test wrong. Doing it correctly I have the same effect as the shadow volumes. The entire cube volume is cute out of the scene rather than just shadowing on the wall.
  6. Just did a pass through my silhouette edges to see if any 2 vertices show up twice in one edge, regardless of ordering, and it doesn't seem to occur, so I don't think that I'm rendering the volume geometry more than once. I could still be wrong about that, but it would have to be happening somewhere else.
  7. Ok, I think I've got some meaningful results here. [quote name='Tsus' timestamp='1326072698' post='4900815'][list] [*]Instead of the shadow volumes render a sphere that intersects with a wall and let its front face increment the stencil values and the back face decrement. This should give you a shadow where the sphere intersects the wall. This way you can see, whether your state setup is causing the problem or the rendering code of your shadow volumes. [/list] [/quote] I rendered a cube bisecting the back wall instead of the shadow volumes and there was no shadow at all. I tried again using the stencil_two_side extension and got the same result, no shadow. Very curious. [quote name='Tsus' timestamp='1326072698' post='4900815'][list] [*]If this doesn’t work use glReadPixel to read the stencil buffer to the CPU, create a color array that maps the stencil values to colors and render this with glDrawPixels. (In D3D is a way to read directly from the stencil buffer in a shader. I’d assume that GL can do this too.) [*]Is it possible that shadow volume faces are drawn twice? (You could see this, if you have a look at the values in the stencil buffer.) [/list] [/quote] I've used this code to render out the stencil buffer as my detail shader texture: [code] boost::shared_array<unsigned char> stencil(new unsigned char[window_width() * window_height() * 4]); glReadPixels(0, 0, _window->w, _window->h, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, stencil.get()); glBindTexture(GL_TEXTURE_2D, _tbo[DetailBuffer]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, window_width(), window_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, stencil.get()); [/code] Having the shader map the red component to all 3 colors (gba seem to all be from the depth buffer) I got this: http://i.imgur.com/95ZXM.png I tried the same thing rendering the cube and there were no white pixels as I moved toward the back wall. [quote name='Tsus' timestamp='1326072698' post='4900815'][list] [*]Don’t use FBOs for now. Try to get it running with the backbuffer, then all in a single FBO and then do the compositing with multiple FBOs (step by step). [/list] [/quote] I sent everything to framebuffer 0 and re-enabled blending, but got the same result as before. [quote name='Tsus' timestamp='1326072698' post='4900815'][list] [*]The render_detail pass should fill the depth buffer anew. GL_EQUAL should be avoided if possible, since it can be very evil due to precision problems. So, clear it and set glDepthFunc(GL_LEQUAL) and glDepthMask(GL_TRUE). If you got it working you can try with GL_EQUAL again. [/list] [/quote] Unfortunately, this didn't change anything either. I figured that what you're saying would be a better idea than turning the mask off and testing for equality, but at this point I'm trying to stick as closely to what I had read in case leaving something out turns out to be a source of the problem. Though it seems like maybe the setup is where I've gone wrong, given that the cube didn't seem to affect the stencil values. Could it be that I am actually doubling up on edges and causing the volume geometry to be rendered twice? They're not affecting the depth buffer, so it would have to be an odd number of renderings in order to cut out the volume like that, wouldn't it?
  8. Yeah, I think it's probably going to be easiest if I stick some code in here. I'm using two FBOs, one for the ambient pass and one for the lighting pass, which I combine at the end in a separate shader. Both FBOs share the same depth/stencil buffer, which I've verified is D24S8, and both are checked for completeness. I've checked both color buffers separately and everything looks ok on that end, so I don't think the FBOs are related to the issue (though I can't discount them). My stencil is also cleared to 1 rather than 0 at the moment, but that should be taken into account when I do the lighting pass and check for a 1 rather than a 0. This is my render function: [code] // render the ambient (filling the depth buffer) glBindFramebuffer(GL_FRAMEBUFFER, _fbo[AmbientBuffer]); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); render_ambient(map); glBindFramebuffer(GL_FRAMEBUFFER, 0); // render the detail glBindFramebuffer(GL_FRAMEBUFFER, _fbo[DetailBuffer]); glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // fill the stencil buffer with shadows if(Light::lighting_enabled()) { render_shadows(map); } // only render where the stencil is 0 and the depth is equal (only modify the color buffer) glDepthMask(GL_FALSE); glDepthFunc(GL_EQUAL); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glStencilFunc(GL_EQUAL, 1, ~0); //glEnable(GL_BLEND); //glBlendFunc(GL_ONE, GL_ONE); render_detail(map); //glDisable(GL_BLEND); glStencilFunc(GL_ALWAYS, 0, ~0); glDepthFunc(GL_LEQUAL); glDepthMask(GL_TRUE); glBindFramebuffer(GL_FRAMEBUFFER, 0); [/code] The blending code is commented out because I'm doing that in the final stage shader. This is the render_shadows method: [code] // disable color and depth writes glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_FALSE); // setup the stencil and depth functions glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glStencilFunc(GL_ALWAYS, 0, ~0); glDepthFunc(GL_LESS); glEnable(GL_CULL_FACE); // offset the shadows /*glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(0.0f, 100.0f);*/ BOOST_FOREACH(boost::shared_ptr<Light> light, map.lights()) { if(!light->enabled()) { continue; } BOOST_FOREACH(boost::shared_ptr<Renderable> renderable, _light_renderables) { if(renderable->has_shadow()) { render_shadow(*renderable, *light); } } } glCullFace(GL_BACK); //glDisable(GL_POLYGON_OFFSET_FILL); glDepthFunc(GL_LEQUAL); glStencilFunc(GL_ALWAYS, 0, ~0); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // re-enable color and depth writes glDepthMask(GL_TRUE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); [/code] and this is the render_shadow method that does each object per-light: [code] glEnable(GL_CULL_FACE); // increment for front faces glCullFace(GL_BACK); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); renderable.render_shadow(shader, light, false); // decrement for back faces glCullFace(GL_FRONT); glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); renderable.render_shadow(shader, light, false); [/code] I'm not currently doing any of the volume capping, but I don't think that should be causing what I'm seeing. This is the renderable's render_shadow method, where I determine the silhouette: [code] Matrix4 matrix; transform(matrix); Renderer::instance().push_model_matrix(); Renderer::instance().multiply_model_matrix(matrix); // Mathematics for 3D Game Programming and Computer Graphics, section 10.3 size_t vstart = 0; for(size_t i=0; i<_model->mesh_count(); ++i) { const MD5Mesh& mesh(_model->mesh(i)); // determine the mesh silhouette std::vector<MD5Edge> silhouette; for(size_t j=0; j<mesh.edge_count(); ++j) { const MD5Edge& edge(mesh.edge(j)); // calculate the plane equation of the faces using the face normal Point4 f1, f2; if(edge.t1 >= 0) { const MD5Triangle& t(mesh.triangle(edge.t1)); const Vertex &v0(_vertices[vstart + t.v1]), &v1(_vertices[vstart + t.v2]), &v2(_vertices[vstart + t.v3]); const Point3 q1(v1.position - v0.position), q2(v2.position - v0.position); const Point3 n1(q1 ^ q2); f1 = Point4(n1, -n1 * _vertices[vstart + t.v1].position); } if(edge.t2 >= 0) { const MD5Triangle& t(mesh.triangle(edge.t2)); const Vertex &v0(_vertices[vstart + t.v1]), &v1(_vertices[vstart + t.v2]), &v2(_vertices[vstart + t.v3]); const Point3 q1(v1.position - v0.position), q2(v2.position - v0.position); const Point3 n2(q1 ^ q2); f2 = Point4(n2, -n2 * _vertices[vstart + t.v2].position); } // sort out which of the edges, if any, are facing the light (with the light converted to object-space) float dt1=0, dt2=0; if(typeid(light) == typeid(DirectionalLight)) { const DirectionalLight& directional(dynamic_cast<const DirectionalLight&>(light)); const Point4 direction(matrix * directional.direction()); dt1 = f1 * direction; dt2 = f2 * direction; } else if(typeid(light) == typeid(PositionalLight)) { const PositionalLight& positional(dynamic_cast<const PositionalLight&>(light)); const Point4 position(matrix * positional.position4()); dt1 = f1 * position; dt2 = f2 * position; } else if(typeid(light) == typeid(SpotLight)) { const SpotLight& spot(dynamic_cast<const SpotLight&>(light)); const Point4 position(matrix * spot.position4()); dt1 = f1 * position; dt2 = f2 * position; } if(edge.t1 >= 0 && edge.t2 >= 0 && ((dt1 >= 0 && dt2 < 0) || (dt1 < 0 && dt2 >= 0))) { // two-winged edges are silhouette edges if one triangle faces towards the light and the other way silhouette.push_back(edge); } else if((edge.t1 < 0 || edge.t2 < 0) && dt1 >= 0) { // one-winged edges are only a silhouette edge if the *first* triangle faces the light silhouette.push_back(edge); } } render_silhouette(shader, mesh, silhouette, vstart, light, cap); vstart += mesh.vertex_count(); } Renderer::instance().pop_model_matrix(); [/code] And this is the rendering of the silhouette: [code] // Mathematics for 3D Game Programming and Computer Graphics, section 10.3/4 // build the vertex buffer const size_t vcount = silhouette.size() * 3; boost::shared_array<float> varray(new float[vcount * 3]), narray(new float[vcount * 3]); float *v = varray.get(), *n = narray.get(); for(size_t i=0; i<silhouette.size(); ++i) { const MD5Edge& edge(silhouette[i]); const Vertex &v1(_vertices[vstart + edge.v1]), &v2(_vertices[vstart + edge.v2]); const size_t idx = i * 3 * 3; // calculate the face normal for the first triangle const MD5Triangle& t(mesh.triangle(edge.t1)); const Vertex &tv0(_vertices[vstart + t.v1]), &tv1(_vertices[vstart + t.v2]), &tv2(_vertices[vstart + t.v3]); const Point3 q1(tv1.position - tv0.position), q2(tv2.position - tv0.position); const Point3 normal(q1 ^ q2); *(v + idx + 0) = v1.position.x; *(v + idx + 1) = v1.position.y; *(v + idx + 2) = v1.position.z; *(v + idx + 3) = v2.position.x; *(v + idx + 4) = v2.position.y; *(v + idx + 5) = v2.position.z; // 3rd vertex doesn't matter *(v + idx + 6) = 0.0f; *(v + idx + 7) = 0.0f; *(v + idx + 8) = 0.0f; // push the face normal to all vertices *(n + idx + 0) = *(n + idx + 3) = *(n + idx + 6) = normal.x; *(n + idx + 1) = *(n + idx + 4) = *(n + idx + 7) = normal.y; *(n + idx + 2) = *(n + idx + 5) = *(n + idx + 8) = normal.z; } // setup the vertex array glBindBuffer(GL_ARRAY_BUFFER, _shadow_vbo[ShadowVertexArray]); glBufferData(GL_ARRAY_BUFFER, vcount * 3 * sizeof(float), varray.get(), GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, _shadow_vbo[ShadowNormalArray]); glBufferData(GL_ARRAY_BUFFER, vcount * 3 * sizeof(float), narray.get(), GL_DYNAMIC_DRAW); shader.begin(); shader.uniform1i("cap", cap); Renderer::instance().init_shader_matrices(shader); Renderer::instance().init_shader_light(shader, light); // get the attribute locations GLint vloc = shader.attrib_location("vertex"); GLint nloc = shader.attrib_location("normal"); // render the silhouette glEnableVertexAttribArray(vloc); glEnableVertexAttribArray(nloc); glBindBuffer(GL_ARRAY_BUFFER, _shadow_vbo[ShadowNormalArray]); glVertexAttribPointer(nloc, 3, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, _shadow_vbo[ShadowVertexArray]); glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, 0); glDrawArrays(GL_TRIANGLES, 0, vcount); glDisableVertexAttribArray(nloc); glDisableVertexAttribArray(vloc); shader.end(); [/code] I'm doing the actual edge extrusion in a geometry shader (this one is for infinite lights): [code] #version 150 uniform mat4 mvp, modelview; // true if we need to cap the shadow uniform bool cap; // these are in eye-space uniform vec4 light_position; layout(triangles) in; layout(triangle_strip, max_vertices=3) out; in vec3 geom_normal[3]; out vec4 frag_color; void main() { // Mathematics for 3D Game Programming and Computer Graphics, section 10.4 // convert the light to object-space vec4 L = inverse(modelview) * light_position; // extrude the silhouette vec4 vertices[3]; float dt = dot(geom_normal[0], L.xyz); if(dt > 0) { frag_color = vec4(1.0, 0.0, 0.0, 1.0); vertices[0] = vec4(gl_in[1].gl_Position.xyz, 1.0); vertices[1] = vec4(gl_in[0].gl_Position.xyz, 1.0); vertices[2] = vec4(0.0); } else { frag_color = vec4(0.0, 0.0, 1.0, 1.0); vertices[0] = vec4(gl_in[0].gl_Position.xyz, 1.0); vertices[1] = vec4(gl_in[1].gl_Position.xyz, 1.0); vertices[2] = vec4(0.0); } for(int i=0; i<vertices.length(); ++i) { gl_Position = mvp * (vertices[i].w * (vertices[i] + L) - L); EmitVertex(); } EndPrimitive(); } [/code] I have a fragment shader attached to that geometry shader so I can render the shadow volume separately and it looks to me like the normals are correct when I color the extruded geometry with them and changing the face culling when I render it looks like what I would expect. Front faces where you want front faces and so on. I really appreciate the help with this. Every example I've found for how to do this seems to be "do it this way and it just works" with no hints on how to debug things when they don't end up working.
  9. I do. When I make my shadow rendering pass, I turn off writes to both the color buffer and the depth buffer. I've read out the depth buffer to disk after the pass completes and it looks the same to me as it does before I start.
  10. Hm, it looks like maybe decrementing the stencil buffer isn't working right. I'm doing this method: [code] // increment for front faces glCullFace(GL_BACK); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); renderable.render_shadow(light, false); // decrement for back faces glCullFace(GL_FRONT); glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); renderable.render_shadow(light, false); [/code] If I comment out the back face step, it looks the same as it does with it kept in. However, if I comment out the front face step then the scene is completely unshadowed. I would expect that if the DECR was happening as it's supposed to, then I would still see the volume as the stencil buffer would be non-zero there.
  11. Here's another shot with a point light source: http://i.imgur.com/7A1Af.png
  12. I've been following the Shadows chapter of Math for 3D Game Programming, but for some reason I'm ending up with a weird problem that I can't seem to find the cause of. Here's a screenshot of what I'm seeing: [url="http://i.imgur.com/At3Xa.png"]http://i.imgur.com/At3Xa.png[/url] The black lines are the shadow volumes for all of the objects in my scene, using a single infinite light source. They're all coming from the stencil buffer (I've dumped the depth buffer between my ambient pass, my shadowing pass, and my lighting pass and it's never modified after the ambient), but I don't understand why I'm seeing the volume instead of just shadows where they intersect geometry. Any ideas what's going on? I'm more than happy to post whatever code would be relevant if that would help. Thanks in advance!
  13. Well, I won't know ahead of time what the variables to replace will be, so a regular expression will probably be better. I'm still just more curious about how to get a for loop to skip values now that I've run across trying to do it.
  14. Yeah, I think that's probably what I'm going to end up doing. I'm basically just going to be replacing the {{name}} with some value in a variable. Now that I've run into this though, I'm curious about how it could be done. The basic goal here is just looking ahead when parsing the string, seeing that the next character is another { and moving the next iteration to the following character.
  15. I've written parsers in C before that would do something like this: char* str = "Hello {{name}}"; for(int i=0; i<strlen(str); ++i) { if(str[i] == '{') { if(str[i+1] == '{') { i++; do_something(); } } } So it ends up skipping over that second character when the for loop iteration var is incremented. I'm trying to do something similar in python, but obviously it's not working because of the way I'm doing the loop over the list: str = "Hello {{name}}" for i in range(len(str)): if str[i] == '{': if str[i+1] == '{': i += 1 do_something() Is there a way to make this work? Thanks in advance.