I want to find normals for height map data. I am using gl_triangles in my code for indices. How would I find normals for this?
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
回答1:
Given a triangle (vert1, vert2, vert3)
its normal is ((vert2 - vert1).cross(vert3 - vert1)).normalize()
.
For smooth, per-vertex normals: Foreach vertex, sum together the face normals for each triangle that vertex is a part of, then normalize the sum.
EDIT: Example:
#include <GL/glut.h>
#include <vector>
#include <cmath>
#include <Eigen/Core>
#include <Eigen/Geometry>
using namespace std;
using namespace Eigen;
typedef Matrix< Vector3f, Dynamic, Dynamic > VecMat;
// given a matrix of heights returns a matrix of vertices
VecMat GetVerts( const MatrixXf& hm )
{
VecMat verts( hm.rows(), hm.cols() );
for( int col = 0; col < hm.cols(); ++col )
for( int row = 0; row < hm.rows(); ++row )
verts( row, col ) = Vector3f( col, row, hm( row, col ) );
return verts;
}
VecMat GetNormals( const VecMat& hm )
{
VecMat normals( hm );
for( int col = 0; col < hm.cols(); ++col )
for( int row = 0; row < hm.rows(); ++row )
{
Vector3f sum( Vector3f::Zero() );
const Vector3f& cur = hm( row, col );
if( row+1 < hm.rows() && col+1 < hm.cols() )
sum += ( hm( row+0, col+1 ) - cur ).cross( hm( row+1, col+0 ) - cur ).normalized();
if( row+1 < hm.rows() && col > 0 )
sum += ( hm( row+1, col+0 ) - cur ).cross( hm( row+0, col-1 ) - cur ).normalized();
if( row > 0 && col > 0 )
sum += ( hm( row+0, col-1 ) - cur ).cross( hm( row-1, col+0 ) - cur ).normalized();
if( row > 0 && col+1 < hm.cols() )
sum += ( hm( row-1, col+0 ) - cur ).cross( hm( row+0, col+1 ) - cur ).normalized();
normals( row, col ) = sum.normalized();
}
return normals;
}
// returns an index array for a GL_TRIANGLES heightmap
vector< unsigned int > GetIndices( int rows, int cols )
{
vector< unsigned int > indices;
for( int col = 1; col < cols; ++col )
for( int row = 1; row < rows; ++row )
{
// Eigen default storage order is column-major
// lower triangle
indices.push_back( (col-1) * rows + (row-1) );
indices.push_back( (col-0) * rows + (row-1) );
indices.push_back( (col-1) * rows + (row-0) );
// upper triangle
indices.push_back( (col-1) * rows + (row-0) );
indices.push_back( (col-0) * rows + (row-1) );
indices.push_back( (col-0) * rows + (row-0) );
}
return indices;
}
VecMat heightmap;
VecMat normals;
vector< unsigned int > indices;
void init()
{
// wavy heightmap
MatrixXf hm( 64, 64 );
for( int col = 1; col < hm.cols(); ++col )
for( int row = 1; row < hm.rows(); ++row )
{
float x = ( col - ( hm.cols() / 2.0f ) ) / 2.0f;
float y = ( row - ( hm.rows() / 2.0f ) ) / 2.0f;
hm( row, col ) = cos( sqrt( x * x + y * y ) );
}
heightmap = GetVerts( hm );
heightmap.array() -= Vector3f( hm.cols() / 2.0f, hm.rows() / 2.0f, 0 );
for( int col = 0; col < hm.cols(); ++col )
for( int row = 0; row < hm.rows(); ++row )
heightmap( row, col ).array() *= Vector3f( 1 / 4.0f, 1 / 4.0f, 1.0f ).array();
normals = GetNormals( heightmap );
indices = GetIndices( heightmap.rows(), heightmap.cols() );
}
void display()
{
glEnable( GL_DEPTH_TEST );
glEnable( GL_CULL_FACE );
glShadeModel( GL_SMOOTH );
glEnable( GL_LIGHTING );
GLfloat global_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, global_ambient );
glEnable( GL_COLOR_MATERIAL );
glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
gluPerspective( 60, w / h, 1, 100 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt( 8, 8, 8, 0, 0, 0, 0, 0, 1 );
// spinning light
glEnable( GL_LIGHT0 );
float angle = 20 * ( glutGet( GLUT_ELAPSED_TIME ) / 1000.0f ) * (3.14159f / 180.0f);
float x = cos( -angle ) * 6;
float y = sin( -angle ) * 6;
GLfloat light_position[] = { x, y, 2, 1.0 };
glLightfv( GL_LIGHT0, GL_POSITION, light_position );
glDisable( GL_LIGHTING );
glPointSize( 5 );
glBegin(GL_POINTS);
glColor3ub( 255, 255, 255 );
glVertex3fv( light_position );
glEnd();
glEnable( GL_LIGHTING );
glColor3ub(255,0,0);
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glVertexPointer( 3, GL_FLOAT, sizeof( Vector3f ), heightmap(0,0).data() );
glNormalPointer( GL_FLOAT, sizeof( Vector3f ), normals(0,0).data() );
glDrawElements( GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, &indices[0] );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
glutSwapBuffers();
}
void timer( int extra )
{
glutPostRedisplay();
glutTimerFunc( 16, timer, 0 );
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "Heightmap" );
init();
glutDisplayFunc( display );
glutTimerFunc( 0, timer, 0 );
glutMainLoop();
return 0;
}