#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <GL/gl.h>
#include <GL/glut.h>
#include <assert.h>
#include <math.h>
#define INF 0x7f7f7f
#define draw(p) \
if ((p).z != NODATA_value) { \
if (mode) \
glTexCoord2f((p).ex, (p).ey); \
else \
glTexCoord1f((p).ez); \
glVertex3f((p).x, (p).y, (p).z); \
}
float theta = 0.0f;
float phi = 1;
// 读取DEM的内容:
int nCols, nRows, NODATA_value;
GLdouble xllcorner, yllcorner, cellSize;
GLdouble minz = INF, maxz = -INF;
// 视点位置
GLdouble centerX, centerY, centerZ;
// 旋转用:
int lastMouseX1 = 0, lastMouseY1 = 0;
int isRotating = 0;
// 平移用:
int lastMouseX2 = 0, lastMouseY2 = 0;
int isTranslate = 0;
// 窗口大小:
int windowWidth = 800, windowHeight = 600;
// 保存纹理时用到:
GLuint textureID;
int mode;
int width, height, n;
unsigned char *image;
typedef struct Point {
GLdouble x, y, z; // 真实的xyz
GLdouble ex, ey, ez; // 归一化的xyz
} Point;
Point **Dem;
int pat_1d[16] = {0x991100, 0xCC1100, 0xC84500, 0xCFC000, 0x54CE96, 0x00CFC0,
0x00FFC0, 0x56D660, 0x23CF00, 0x00C100, 0x26972F, 0x218528,
0x1B824C, 0x3D6283, 0x65949f, 0xF6F1E9};
GLubyte texture[16][3];
void readData(const char *fileName) {
FILE *fp = fopen(fileName, "r");
assert(fp != NULL);
// 读取文件头
fscanf(fp, "ncols%d\n", &nCols);
fscanf(fp, "nrows%d\n", &nRows);
fscanf(fp, "xllcorner%lf\n", &xllcorner);
fscanf(fp, "yllcorner%lf\n", &yllcorner);
fscanf(fp, "cellsize%lf\n", &cellSize);
fscanf(fp, "NODATA_value%d\n", &NODATA_value);
Dem = (Point **)malloc(sizeof(Point *) * nRows);
for (int i = 0; i < nRows; ++i)
Dem[i] = (Point *)malloc(sizeof(Point) * nCols);
double cury = yllcorner;
for (int i = 0; i < nRows; ++i, cury += cellSize) {
double curx = xllcorner;
for (int j = 0, temp; j < nCols; ++j, curx += cellSize) {
Dem[i][j] = (Point){.x = curx, .y = cury};
fscanf(fp, "%d", &temp);
if (temp != NODATA_value)
Dem[i][j].z = temp / 1000.0;
else
Dem[i][j].z = NODATA_value;
}
}
fclose(fp);
}
// 根据模式创建对应的纹理
static void createTexture() {
glDeleteTextures(1, &textureID);
glGenTextures(1, &textureID);
// 创建一阶纹理
if (!mode) {
glBindTexture(GL_TEXTURE_1D, textureID);
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 16, 0, GL_RGB, GL_UNSIGNED_BYTE,
texture);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
} else {
glBindTexture(GL_TEXTURE_2D, textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
if (image) {
// 设置纹理参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
printf("%d %d\n", width, height);
// 为纹理对象提供数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB,
GL_UNSIGNED_BYTE, image);
}
}
}
// 绘制三角形
static void drawTriangles(int i, int j) {
glBegin(GL_TRIANGLES);
draw(Dem[i][j]);
draw(Dem[i + 1][j]);
draw(Dem[i + 1][j + 1]);
glEnd();
glBegin(GL_TRIANGLES);
draw(Dem[i][j]);
draw(Dem[i + 1][j + 1]);
draw(Dem[i][j + 1]);
glEnd();
}
void display(void) {
glClear(GL_COLOR_BUFFER_BIT);
// 模型变换
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// 透视变换
gluPerspective(45.0, 1.0, 100, 3000.0);
double cameraDistance =
sqrt((centerX - xllcorner) * (centerX - xllcorner) +
(centerY - yllcorner) * (centerY - yllcorner) + 1.21 * maxz * maxz);
// 根据球坐标系计算相机的位置
double cameraX = cameraDistance * cos(theta) * cos(phi) + centerX;
double cameraZ = cameraDistance * sin(phi) + centerZ;
double cameraY = cameraDistance * sin(theta) * cos(phi) + centerY;
// 视点变换
gluLookAt(cameraX, cameraY, cameraZ, centerX, centerY, centerZ, 0, 0, 1);
// 创建纹理
createTexture();
// 切换二维和一维纹理
if (mode)
glEnable(GL_TEXTURE_2D);
else
glEnable(GL_TEXTURE_1D);
// 绘制三角形
for (int i = 0; i < nRows - 1; ++i)
for (int j = 0; j < nCols - 1; ++j)
drawTriangles(i, (j));
glFlush();
}
void reshape(int width, int height) {
windowWidth = width;
windowHeight = height;
glViewport(0, 0, width, height);
glutPostRedisplay();
}
void mouse(int button, int state, int x, int y) {
if (button == GLUT_RIGHT_BUTTON) { // 左移动右转动
if (state == GLUT_DOWN) {
isRotating = 1;
lastMouseX1 = x;
lastMouseY1 = y;
} else if (state == GLUT_UP)
isRotating = 0;
} else if (button == GLUT_LEFT_BUTTON) {
if (state == GLUT_DOWN) {
isTranslate = 1;
lastMouseX2 = x;
lastMouseY2 = y;
} else if (state == GLUT_UP)
isTranslate = 0;
} else if (button == GLUT_MIDDLE_BUTTON) {
if (state == GLUT_DOWN) {
mode = !mode;
glutPostRedisplay();
}
}
}
void motion(int x, int y) {
if (isRotating) {
int deltaX = x - lastMouseX1;
theta += (float)(deltaX)*0.01f;
int deltaY = y - lastMouseY1;
phi += (float)(deltaY)*0.01f;
lastMouseX1 = x;
lastMouseY1 = y;
glutPostRedisplay();
}
if (isTranslate) {
int deltaX = x - lastMouseX2;
int deltaY = y - lastMouseY2;
centerX += deltaX;
centerY += deltaY;
lastMouseX2 = x;
lastMouseY2 = y;
glutPostRedisplay();
}
}
void init() {
// 归一化坐标:
for (int i = 0; i < nRows; ++i)
for (int j = 0; j < nCols; ++j)
if (Dem[i][j].z != NODATA_value && Dem[i][j].z < minz)
minz = Dem[i][j].z;
for (int i = 0; i < nRows; ++i)
for (int j = 0; j < nCols; ++j)
if (Dem[i][j].z > maxz)
maxz = Dem[i][j].z;
centerZ = maxz;
centerX = xllcorner + nCols * cellSize / 2.0;
centerY = yllcorner + nRows * cellSize / 2.0;
for (int i = 0; i < nRows; ++i)
for (int j = 0; j < nCols; ++j)
if (Dem[i][j].z != NODATA_value) {
Dem[i][j].ex = (Dem[i][j].x - xllcorner) / (nCols * cellSize);
Dem[i][j].ey = (Dem[i][j].y - yllcorner) / (nRows * cellSize);
Dem[i][j].ez = (Dem[i][j].z - minz) / (maxz - minz);
}
// 预处理一阶纹理
for (int i = 0; i < 16; ++i) {
texture[i][2] = (pat_1d[i] >> 16) & 0xFF; // 红色分量
texture[i][1] = (pat_1d[i] >> 8) & 0xFF; // 绿色分量
texture[i][0] = pat_1d[i] & 0xFF; // 蓝色分量
}
image = stbi_load("dom.jpg", &width, &height, &n, 0);
}
int main(int argc, char **argv) {
readData("dem.asc");
init();
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(windowWidth, windowHeight);
glutCreateWindow("Surface");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutMainLoop();
stbi_image_free(image); // 释放图像数据
for (int i = 0; i < nRows; ++i)
free(Dem[i]);
free(Dem);
return 0;
}