计算机图形学练习(三)——画一个可以由键盘控制的球

标签: 计算机图形学  图形学

画一个可以由键盘控制的球

在这里插入图片描述
第一步:计算z坐标

球面绘制,首先按z方向,切分成多节,每节就是一个圆周;就是上图的1截出来的部分就是2所示的圆周。
截出的圆周有一个高,就是z-坐标;
圆周的高,其实就是这个圆周与圆心形成的锥形的角度决定了圆周的截断高度。这样可以轻松计算出
第二步:等高截的圆周的半径r

这个圆周半径也容易计算
第三步:计算圆周上的x,y

圆周半径确定的情况下,x,y容易计算

链接:https://www.jianshu.com/p/48a4b3f3d51e
来源:简书

#include <iostream>
#define GLEW_STATIC
#include <GL/glew.h>

#include <GLFW/glfw3.h>

#include "Shader.h"

#include "SOIL2/SOIL2.h"  
#include "SOIL2/stb_image.h"

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>  
#include <glm/gtc/type_ptr.hpp>

const GLint WIDTH = 800, HEIGHT = 600; 
float x = 0;
float y = 0;
float temp = 0;
bool keys[1024];  //存放获取的所有键盘操作,先存下来再进行操作

void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
		glfwSetWindowShouldClose(window, GL_TRUE);  //设定关闭窗口
	}

	if (key >= 0 && key < 1024) {
		if (action == GLFW_PRESS)
		{
			keys[key] = true;  //键盘按下去了,就设置为 true,即为1
		}
		else if (action == GLFW_RELEASE)
		{
			keys[key] = false;  //键盘松开,设为 false
		}
	}
}

void DoMovement()
{
	if (keys[GLFW_KEY_W] || keys[GLFW_KEY_UP]) {
		y += 0.001;
	}
	if (keys[GLFW_KEY_SPACE]) {
		temp = y;
		y += 0.5;
	}
	if (keys[GLFW_KEY_S] || keys[GLFW_KEY_DOWN]) {
		y -= 0.001;
	}
	if (keys[GLFW_KEY_A] || keys[GLFW_KEY_LEFT]) {
		x -= 0.001;
	}
	if (keys[GLFW_KEY_D] || keys[GLFW_KEY_RIGHT]) {
		x += 0.001;
	}
}

glm::vec3  getPoint(GLfloat u, GLfloat v) {
	GLfloat r = 0.9f;
	GLfloat pi = glm::pi<GLfloat>();
	GLfloat z = r * std::cos(pi * u);
	GLfloat x = r * std::sin(pi * u) * std::cos(2 * pi * v);
	GLfloat y = r * std::sin(pi * u) * std::sin(2 * pi * v);
	return glm::vec3(x, y, z);
}

void createSphere(GLfloat* sphere, GLuint Longitude, GLuint Latitude) {
	// Longitude:经线切分个数
	// Latitude:纬线切分个数
	GLfloat lon_step = 1.0f / Longitude;
	GLfloat lat_step = 1.0f / Latitude;
	GLuint offset = 0;
	for (int lat = 0; lat < Latitude; lat++) {  // 纬线u
		for (int lon = 0; lon < Longitude; lon++) { // 经线v
			// 一次构造4个点,两个三角形,
			glm::vec3 point1 = getPoint(lat * lat_step, lon * lon_step);
			glm::vec3 point2 = getPoint((lat + 1) * lat_step, lon * lon_step);
			glm::vec3 point3 = getPoint((lat + 1) * lat_step, (lon + 1) * lon_step);
			glm::vec3 point4 = getPoint(lat * lat_step, (lon + 1) * lon_step);
			memcpy(sphere + offset, glm::value_ptr(point1), 3 * sizeof(GLfloat));
			offset += 3;
			memcpy(sphere + offset, glm::value_ptr(point4), 3 * sizeof(GLfloat));
			offset += 3;
			memcpy(sphere + offset, glm::value_ptr(point3), 3 * sizeof(GLfloat));
			offset += 3;

			memcpy(sphere + offset, glm::value_ptr(point1), 3 * sizeof(GLfloat));
			offset += 3;
			memcpy(sphere + offset, glm::value_ptr(point3), 3 * sizeof(GLfloat));
			offset += 3;
			memcpy(sphere + offset, glm::value_ptr(point2), 3 * sizeof(GLfloat));
			offset += 3;
		}
	}
}

int main()
{
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);  
	GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Learn OpenGL", nullptr, nullptr);

	if (nullptr == window)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}

	int screenWidth, screenHeight;
	glfwGetFramebufferSize(window, &screenWidth, &screenHeight);  //获取窗口大小

	glfwMakeContextCurrent(window);  //可以新建很多 window
	glfwSetKeyCallback(window, KeyCallback);

	glewExperimental = GL_TRUE;
	if (GLEW_OK != glewInit())
	{
		std::cout << "Failed to initialize GLEW" << std::endl;
		return -1;
	}
	glViewport(0, 0, screenWidth, screenHeight);  //从(0,0)开始画点,直到 WIDTH 和 HEIGHT

	Shader ourShader = Shader("res/shaders/code.vs", "res/shaders/code.fs");  //文件相对路径

	GLuint lats = 30;
	GLuint lons = 60;
	GLfloat vertices[6 * 3 * 30 * 60];

	createSphere(vertices, lons, lats);

	//创建VAO和VBO并绑定 传到显存
	GLuint VAO, VBO;  //VAO:Vertex Array Object   VBO:Vertex Buffer Object传数据
	glGenVertexArrays(1, &VAO);  //创建 VAO
	glGenBuffers(1, &VBO);
	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);  //VAO 和 VBO 成对出现
	// transfer the data:传数据
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);  //静态访问,几乎不修改

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
	glEnableVertexAttribArray(0);
	
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
	glEnableVertexAttribArray(1);

	//解绑VAO和VBO
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
	//画图
	while (!glfwWindowShouldClose(window))
	{
		glfwPollEvents();  //把所有事件系统都取过来:键盘/鼠标等操作
		DoMovement();  //获取完操作之后的额外参数
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);  //窗口背景颜色,RGB,最后一个是透明度
		glClear(GL_COLOR_BUFFER_BIT);

		ourShader.Use();
		glm::mat4 transform = glm::mat4(1.0f);  //初始化 4 * 4 单位矩阵

		//缩放,x、y、z 都缩放到原来的 0.5 倍
		transform = glm::scale(transform, glm::vec3(0.5f, 0.5f, 0.0f));

		//平移
		//double x = 0;
		//不要移出屏幕
		if (x >= 1)x = 1;
		if (x <= -1)x = -1;
		if (y >= 1)y = 1;
		if (y <= -1)y = -1;
		transform = glm::translate(transform, glm::vec3(x, y, 0.0f));
		if (keys[GLFW_KEY_SPACE]) {
			y = temp;//回到原来的位置
		}


		GLuint transLoc = glGetUniformLocation(ourShader.Program, "transform");  //在vs 找到那个 transform 变量
		glUniformMatrix4fv(transLoc, 1, GL_FALSE, glm::value_ptr(transform));
		//Matrix4fv:4维矩阵,fv:浮点类型
		//transLoc:变量 transform 的位置
		//1:代表只传入一个矩阵
		//GL_FALSE:不对矩阵进行置换,即不交换矩阵的行和列。GLM 的默认布局就是列主序,所以并不需要置换矩阵
		//最后:直接给出 transform 矩阵数组,这里我们要把矩阵转换成数组的格式传递。
		//glUniformMatrix4fv:四个坐标  glUniform4fv:三个坐标
		
		//开始画图
		glBindVertexArray(VAO);
		glDrawArrays(GL_TRIANGLES, 0, 6 * 30 * 60);  //画三角形,从第 0 个数据开始画,到最后一个数据(第 3 个)结束
		
		glBindVertexArray(0);

		glfwSwapBuffers(window);  //调用双面进行画,显示一个,另一个在画,画面更流畅
	}
	
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	glfwTerminate();
	return 0;
}

Shader.h

#pragma once

#include <string.h>
#include <fstream>
#include <sstream>
#include <iostream>

#include <GL/glew.h>

class Shader
{
	GLuint vertex, fragment;
public:
	GLuint Program;
	Shader(const GLchar* vertexPath, const GLchar* fragmentPath)
	{
		std::string vertexCode;
		std::string fragmentCode;
		std::ifstream vShaderFile;
		std::ifstream fShaderFile;

		vShaderFile.exceptions(std::ifstream::badbit);
		fShaderFile.exceptions(std::ifstream::badbit);

		try
		{
			vShaderFile.open(vertexPath);
			fShaderFile.open(fragmentPath);

			std::stringstream vShaderStream, fShaderStream;
			vShaderStream << vShaderFile.rdbuf();
			fShaderStream << fShaderFile.rdbuf();

			vShaderFile.close();
			fShaderFile.close();

			vertexCode = vShaderStream.str();
			fragmentCode = fShaderStream.str();

		}
		catch (std::ifstream::failure e)
		{
			std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ" << std::endl;
		}
		const GLchar* vShaderCode = vertexCode.c_str();
		const GLchar* fShaderCode = fragmentCode.c_str();

		//compile Shaders
		GLint success;
		GLchar infoLog[512];
		vertex = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vertex, 1, &vShaderCode, NULL);
		glCompileShader(vertex);

		glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
		if (!success) {
			glGetShaderInfoLog(vertex, 512, NULL, infoLog);
			std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n"
				<< infoLog << std::endl;
		}

		fragment = glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fragment, 1, &fShaderCode, NULL);
		glCompileShader(fragment);

		glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
		if (!success) {
			glGetShaderInfoLog(fragment, 512, NULL, infoLog);
			std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n"
				<< infoLog << std::endl;
		}

		this ->Program = glCreateProgram();
		glAttachShader(this->Program, vertex);
		glAttachShader(this->Program, fragment);
		glLinkProgram(this->Program);

		glGetProgramiv(this->Program, GL_LINK_STATUS, &success);
		if (!success) {
			glGetProgramInfoLog(this->Program, 512, NULL, infoLog);
			std::cout << "ERROR::SHADER::PROGRAM::LINK_FAILED\n"
				<< infoLog << std::endl;
		}
	}
	~Shader() {
		glDetachShader(this->Program, vertex);
		glDetachShader(this->Program, fragment);
		glDeleteShader(vertex);
		glDeleteShader(fragment);
		glDeleteProgram(this->Program);
	}
	void Use()
	{
		glUseProgram(this->Program);
	}
};

code.vs

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;

out vec3 Color;
uniform mat4 transform;

void main(){
    gl_Position = transform * vec4(position, 1.0f);
	Color = color;
}

code.fs

#version 330 core
in vec3 Color;
out vec4 color;

void main(){
   color = vec4(Color, 0.8f);
}

运行结果

在这里插入图片描述

版权声明:本文为qq_44018760原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_44018760/article/details/109706612

智能推荐

B1105 Spiral Matrix (画图)

B1105 Spiral Matrix (25分) //第一次只拿了21分 矩阵的长和宽,求最大因子,从sqrt(num)开始枚举. 每次循环一次,s++,t--,d--,r++ 测试点四运行超时,是因为输入一个数字的时候,需要直接输出这个数字。//1分 测试点二运行超时,最后一个数字不必再while循环一次,直接输出即可。//3分 最后一个测试点卡了好久/(ㄒoㄒ)/~~ 螺旋矩阵...

Java基础=>String,StringBuffer与StringBuilder的区别

字符串常量池 什么是字符串常量池? JVM为了减少字符串对象的重复创建,其维护了一块特殊的内存,这段内存被称为字符串常量池(存储在方法区中)。 具体实现 当代码中出现字符串时,JVM首先会对其进行检查。 如果字符串常量池中存在相同内容的字符串对象,如果有,则不再创建,直接返回这个对象的地址返回。 如果字符串常量池中不存在相同内容的字符串对象,则创建一个新的字符串对象并放入常量池,并返回新创建的字符...

java调用其他java项目的Https接口

项目中是这样的: 用户拿出二维码展示,让机器识别二维码, 机器调用开门的后台系统接口, 然后开门的后台系统接口需要调用管理系统的接口, 管理系统需要判断能不能开门.这两个系统是互相独立的.当时使用http调用是没有问题的.当时后来要求必须用https.废话不说,直接代码: 我的项目中调用的是 HttpsUtils.Get(utlStr) 这个接口 开门系统接口如下图:   管理系统的接口...

Hadoop1.2.1全分布式模式配置

一 集群规划 主机名            IP                               安装的软件 &nbs...

Go语言gin框架的安装

尝试安装了一下gin,把遇到的一些小问题来记录一下 安装步骤 首先来看看官方文档,链接点这里 可以看到安装步骤很简单,就一句话 在命令行中输入这句话运行等待就好。 问题来了,因为墙的问题,go get会很慢,所以命令行里面半天什么反应也没有,不要急,慢慢等着就会看到gin-gonic/gin这个目录出现 这个时候命令行还是没有结束,表示还在下一些东西。有的时候可能心急的人就停了(比如我),然后写个...

猜你喜欢

uni-app表单组件二

input(输入框) 属性名 类型 说明 平台差异 value String 输入框的初始内容 type String input 的类型 password Boolean(默认false) 是否是密码类型 placeholder String 输入框为空时占位符 placeholder-style String 指定 placeholder 的样式 placeholder-class Strin...

深入理解 JavaScript 代码执行机制

深入理解 JavaScript 代码执行机制 前言 本文仅为个人见解,如有错误的地方欢迎留言区探讨和指正。 1、餐前甜品 如下是一段 JavaScript 代码,如果你毫不犹豫的说出代码执行顺序。那么请直接滚动到底部,留下你的足迹,接受膜拜。如果还不是很确定,那么请往下继续查看。 2、磨刀不误砍柴工(了解浏览器原理) (1) 进程和线程 进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小...

Centos7下配置DRBD Cluster扩展节点

操作环境 CentOS Linux release 7.4.1708 (Core) DRBDADM_BUILDTAG=GIT-hash:\ ee126652638328b55dc6bff47d07d6161ab768db\ build\ by\ [email protected]\,\ 2018-07-30\ 22:23:07 DRBDADM_API_VERSION=2 DRBD_KERNEL_VER...

选择排序了解一下

选择排序是一种简单直观的排序算法,它的主要思想:初始时在序列中找到最小(大)的元素,放到序列的起始位置作为已排序序列;然后再从剩余未排序元素中继续寻找最小(大)的元素,放到已排序序列的末尾,以此类推,直到所有元素均排序完毕。 即每遍历一次就记住了最大(小)的元素的位置,最后仅需要一次交换操作就可以放到其适合的位置。 如下图所示: 实现代码如下: 选择排序是不稳定排序,时间复杂度在最优、最坏情况下都...