• 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.
Sign in to follow this  
Followers 0
CC Ricers

Updating vertex normals on the CPU versus GPU

7 posts in this topic

Right now I am taking care of the final steps to make a terrain geo mipmapping system. It creates all vertex data for every mesh LOD at loading time. There are two concerns with this- the CPU time required to calculate normals, tangents and bitangents when creating the meshes for each terrain tile, and the memory footprint it takes.

 

Because I was careless with putting buffer creation into too many loops, I ran into OutOfMemory exceptions before the code was stable. Doing more GPU work would also be helpful when I start on the terrain editor so that vertices are updated quickly with minimal lag.

 

So to alleviate these two problems to some degree I want to know if there are huge benefits to calculate some of the vertex data on the GPU in real-time. I have a custom vertex declaration structure that's 56 bytes per vertex, and on 2048x2048 maps, this can easily exceed 300 megabytes (lower LOD meshes included). That structure includes Position, Texture Coordinate, Normal, Bitangent and Tangent. It might be possible to reduce this to Position and Texture Coordinate which would be 20 bytes per vertex and have the shaders figure out the other three values.

 

I'm using XNA so geometry shaders are not available. I know it's possible to compute normals using partial derivatives, but for a non-faceted shading look, I don't know how to achieve this.

 

My first thought is to have a stream with a vertex buffer containing the XZ locations, and a transformation matrix for each tile so each terrain LOD can share the same buffer. Then I can either have a separate stream with Y (height) locations to push up each vertex. I'm just not sure if or how it's possible to look up adjacent vertices in the shader to calculate normal data per vertex.

0

Share this post


Link to post
Share on other sites

Do you really need the tangent and bitangent?  At the very least, you could just supply the normal and tangent, and then calculating the bitangent is just a cross product away, but unless you have algorithms that actually need the data then I would just dump them...

 

Have you also considered using instancing to get your vertex data into the pipeline?  This would let you have just one grid representation for each of the LOD levels, and then you would have instance vertex buffers to generate each instance in your terrain, and you could sample from a buffer / texture in your vertex shader to get the rest of the specific data (vertex height, normal, tangent and bitangent (see above...)).  This would cut your position information way down by somewhere around 2/3.

1

Share this post


Link to post
Share on other sites

Hey Jason, so I took your advice and removed some of the vertex element components like the bitangent and computing that on the vertex shader. I also noticed the texture coordinate wasn't being used in the shaders anymore since switching to triplanar texture mapping. So I removed that element also, and compute the tangent by way of normal direction on the CPU.  Then I started reduced space needed even further by making all the tiles of a given LOD use the same index buffer. Right now the program uses about 80mb less memory. I could do better, but it's a start. I was mainly stuck with how to offload tangent/bitangent calculation off to the GPU since I overlooked how the bitangents are calculated. Then all the optimizations became more straightforward.

 

When you said to use instancing for the vertex buffers, that's about what I was thinking too. Since all vertices of a given tile LOD look the same from the top down I only the XZ coordinates would matter for the reusable buffers. So handful of small vertex buffers with just XZ coordinates, and each tile has its own Y coordinate vertex buffer with a world position to transform the tile's mesh into world space. I'm also getting my ideas from this map editor which is helping out on streamlining the data. Would you consider using then, the Y paired with world position as the second vertex stream?

 

However right now I would just keep a non-instancing version and have an instancing version for the best configuration, as I plan to port to MonoGame and that doesn't support instancing in its current state.

0

Share this post


Link to post
Share on other sites

You can even get rid of the position altogether - if you pass the 'tile' position and scale as constants to your vertex shader, then you can use the SV_VertexID to generate the X and Z coordinates, and then look up the Y coordinate from a shader resource view (probably a buffer...).  I would just sample/load the Y data from a resource in the shader, rather than adding it as another vertex buffer.

 

I think that would save a significant amount of memory, and cost very little to generate in the shader.  Note that if you use instancing, you would have to pass the position and scale information as per-instance vertex data, but the memory used is still significantly lower than directly putting it into the per-vertex format.

0

Share this post


Link to post
Share on other sites
Additionally, your vertices are too large. Our verts that contain pos+uv+nrm+tan+bin are 28 bytes. Use compressed (4 byte) encodings for normal, tangent and binormal. Use half-precision (2 bytes per component) for UVs.
0

Share this post


Link to post
Share on other sites

Osmanb, I just now realize you can encode the normals in a more compact format as you could with a G-Buffer, but I already figured out a solution for that. I completely removed UVs as my texture mapping is now triplanar and UV lookup is all normals-based and on the shader.

 

Jason Z, I cannot use the SV_VertexID semantic because it's not supported in DirectX 9/XNA, but I'll keep it in mind for a future project. 

0

Share this post


Link to post
Share on other sites

Osmanb, I just now realize you can encode the normals in a more compact format as you could with a G-Buffer, but I already figured out a solution for that. I completely removed UVs as my texture mapping is now triplanar and UV lookup is all normals-based and on the shader.

 

Jason Z, I cannot use the SV_VertexID semantic because it's not supported in DirectX 9/XNA, but I'll keep it in mind for a future project. 

You could still just use a single vertex attribute to identify the vertex order, then proceed in the same fashion.  You will still remove the majority of the position data from your vertex format, and it can be replaced with an ID that is sized according to the max number of vertices in an instance.  This should be safely less than what can fit into a 16-bit integer...

1

Share this post


Link to post
Share on other sites

Thanks for your help, everyone. I have moved the calculation of tangents and bitangents to the vertex shader with no real impact on performance. And I have settled on a vertex format that I will be later using, which takes up 8 bytes in a format of four short integers. Two for the vertex ID and height, and two for encoded normals as either 2 HalfVectors or 2 normalized short values. 16 bit precision should still be good enough for normals, I would think.

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0