Sign in to follow this  

understanding Frank Luna's geometrical plane rendering with hills

This topic is 2783 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

I am reading Introduction to 3d programming with DirectX10 by Frank Luna. At the end of chapter 5 he explains how to render a 3d plane on the xz axis. the following links are links to images of the pages. http://i159.photobucket.com/albums/t150/numerical25/firstPage.jpg http://i159.photobucket.com/albums/t150/numerical25/secondPage.jpg I started off having this post as a question but turns out I am getting a better understanding of what he is doing. I will have particular questions about things I do not understand at the end. What Frank is trying to do in the code is create a m X n plane laying on the x and z axis. Where n and m represent rows and columns. Below is the code
//=======================================================================================
// PeaksAndValleys.cpp by Frank Luna (C) 2008 All Rights Reserved.
//=======================================================================================

#include "PeaksAndValleys.h"
#include "Vertex.h"
#include <vector>

PeaksAndValleys::PeaksAndValleys()
: mNumRows(0), mNumCols(0), mNumVertices(0), mNumFaces(0),
  md3dDevice(0), mVB(0), mIB(0)
{
}

PeaksAndValleys::~PeaksAndValleys()
{
	ReleaseCOM(mVB);
	ReleaseCOM(mIB);
}

float PeaksAndValleys::getHeight(float x, float z)const
{
	return 0.3f*( z*sinf(0.1f*x) + x*cosf(0.1f*z) );
}

void PeaksAndValleys::init(ID3D10Device* device, DWORD m, DWORD n, float dx)
{
	md3dDevice = device;

	mNumRows  = m;
	mNumCols  = n;

	mNumVertices = m*n;
	mNumFaces    = (m-1)*(n-1)*2;


	// Create the geometry and fill the vertex buffer. 

	std::vector<Vertex> vertices(mNumVertices);
	float halfWidth = (n-1)*dx*0.5f;
	float halfDepth = (m-1)*dx*0.5f;
	for(DWORD i = 0; i < m; ++i)
	{
		float z = halfDepth - i*dx;
		for(DWORD j = 0; j < n; ++j)
		{
			float x = -halfWidth + j*dx;

			// Graph of this function looks like a mountain range.
			float y = getHeight(x,z);

			vertices[i*n+j].pos = D3DXVECTOR3(x, y, z);

			// Color the vertex based on its height.
			if( y < -10.0f )
				vertices[i*n+j].color = BEACH_SAND;
			else if( y < 5.0f )
				vertices[i*n+j].color = LIGHT_YELLOW_GREEN;
			else if( y < 12.0f )
				vertices[i*n+j].color = DARK_YELLOW_GREEN;
			else if( y < 20.0f )
				vertices[i*n+j].color = DARKBROWN;
			else
				vertices[i*n+j].color = WHITE;
		}
	}
 
    D3D10_BUFFER_DESC vbd;
    vbd.Usage = D3D10_USAGE_IMMUTABLE;
    vbd.ByteWidth = sizeof(Vertex) * mNumVertices;
    vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    vbd.CPUAccessFlags = 0;
    vbd.MiscFlags = 0;
	D3D10_SUBRESOURCE_DATA vinitData;
    vinitData.pSysMem = &vertices[0];
    HR(md3dDevice->CreateBuffer(&vbd, &vinitData, &mVB));


	// Create the index buffer.  The index buffer is fixed, so we only 
	// need to create and set once.

	std::vector<DWORD> indices(mNumFaces*3); // 3 indices per face

	// Iterate over each quad and compute indices.
	int k = 0;
	for(DWORD i = 0; i < m-1; ++i)
	{
		for(DWORD j = 0; j < n-1; ++j)
		{
			indices[k]   = i*n+j;
			indices[k+1] = i*n+j+1;
			indices[k+2] = (i+1)*n+j;

			indices[k+3] = (i+1)*n+j;
			indices[k+4] = i*n+j+1;
			indices[k+5] = (i+1)*n+j+1;

			k += 6; // next quad
		}
	}

	D3D10_BUFFER_DESC ibd;
    ibd.Usage = D3D10_USAGE_IMMUTABLE;
    ibd.ByteWidth = sizeof(DWORD) * mNumFaces*3;
    ibd.BindFlags = D3D10_BIND_INDEX_BUFFER;
    ibd.CPUAccessFlags = 0;
    ibd.MiscFlags = 0;
    D3D10_SUBRESOURCE_DATA iinitData;
    iinitData.pSysMem = &indices[0];
    HR(md3dDevice->CreateBuffer(&ibd, &iinitData, &mIB));
}

void PeaksAndValleys::update(float dt)
{
}

void PeaksAndValleys::draw()
{
	UINT stride = sizeof(Vertex);
	UINT offset = 0;
	md3dDevice->IASetVertexBuffers(0, 1, &mVB, &stride, &offset);
	md3dDevice->IASetIndexBuffer(mIB, DXGI_FORMAT_R32_UINT, 0);
    md3dDevice->DrawIndexed(mNumFaces*3, 0, 0);
}
Below I am going to try to break down what he is doing. from within init() some of The parameters he passed through init() are m and n which hold values of 129 and 129 m and n = rows and columns on the plane. n = 129 m = 129 dx = 1.0f (This represents the width of each square square. it is 1 unit long by 1 unit wide) Since each square is equal and height and width, he uses dx to represent both height of width
float halfWidth = (n-1)*dx*0.5f;
float halfDepth = (m-1)*dx*0.5f;
given the information above, we do the following formular float halfWidth = (129 - 1) = 128 * 1 = 128 * 0.5 = 64 float halfDept = (129 - 1) = 128 * 1 = 128 * 0.5 = 64 so... float halfWidth = 64; float halfDept = 64; Since the plane's origin is at 0,0. to get the top left point he must first divide the plane in half.
float z = halfWidth - i*dx;
(64) = 64 - 0 * 1 = -64 z = 64 then he goes inside the another loop which represents each x column and does the samething
float x = -halfWidth + j*dx;
-1(64) = -64 + 0 * 1 = -64 x = -64 this time he negates 64 so we start off on the left side of the plane and not the right (j * dx) and (i * dx) in above's code serves as a incrementer. each loop j increments by 1 -1(64) = -64 + 0 * 1 = -64 -1(64) = -64 + 1 * 1 = -63 -1(64) = -64 + 2 * 1 = -62 ..... and below is for the z axis (notice he subtracts (i * 1) (64) = 64 - 0 * 1 = 64 (64) = 64 - 1 * 1 = 63 (64) = 64 - 2 * 1 = 62 ..... so each loop we move closer to the origin on both axes each loop he works on all columns before moving on to the next row. for each column he calls on the getHeight(x,z) function where he passes the current column and row
float PeaksAndValleys::getHeight(float x, float z)const
{
	return 0.3f*( z*sinf(0.1f*x) + x*cosf(0.1f*z) );
}
assuming this is the first loop, it does the following 0.3f * (64 * sinf(0.1f*-64) + (-64)*cosf(0.1f*64)); I am not sure what the final answer is cause I am not sure how sinf() and cosf() works but I assume that the farther away from the origin, the bigger the value be on the negative or positive side. It then returns a value for y and adds the x y and z value to that vertices. depending on how high your y value is, or how low, gives that vector a particular color. the higher it is , the more it turns white like a mountain peak, the lower it is , the more sandy it is like a beach. after its done, it adds it to the buffer and then adds the indices. If someone could explain why you have to add indices along with the buffer that would be great. and possibly the understanding of sinf and cosf. can't find it on my calculator. But what ever it is, it gives a smooth mountain and valley affect.

Share this post


Link to post
Share on other sites
sinf and cosf are just sin and cos with float precision. Indices are a way to reduce traffice across the bus...

If I have 4 triangles i could either send 12 vertices to the graphics card, 0r just 5 vertices(top left, top right, bottom left, bottom right, and center), and 12 indices. (or less if you render strips or fans)

A vert with position data is 3 floats, so:

12 verts = 36 floats, or 144 bytes,

where as an indice is normally a short(2 bytes, though could use any type) so:

12 indices + 5 verts = 12 shorts + 15 floats, 66 bytes


Share this post


Link to post
Share on other sites

This topic is 2783 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.

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