(第三章)OpenGL超级宝典学习:认识渲染管线

news2025/1/16 2:02:22
OpGL超级宝典学习:认识渲染管线

前言

本章作为OpenGL学习的第三章节
在本章节我们将认识OpenGL的渲染管线
对管线内各个过程有一个初步的认识


★提高阅读体验★

👉 ♠一级标题 👈

👉 ♥二级标题 👈

👉 ♥ 三级标题 👈

👉 ♥ 四级标题 👈


目录

  • ♠ 管线认知
    • ♥ 顶点着色器
    • ♥ 细分曲面
      • ♣ 细分曲面控制着色器
      • ♣ 固定函数细分曲面引擎
      • ♣ 细分曲面评估着色器
      • ♣ 总结
    • ♥ 几何着色器
    • ♥ 基元装配、裁剪和光栅化
      • ♣ 基元装配
      • ♣ 裁剪
        • ♦ 齐次坐标
        • ♦ 标准化设备空间
      • ♣ 视口转化
      • ♣ 剔除
      • ♣ 光栅化
    • ♥ 片段着色器
    • ♥ 帧缓存运算
      • ♣ 像素运算
    • ♥ 计算着色器
  • ♠ 数据传递
    • ♥ in和out关键字
    • ♥ 设置输入</u>
    • ♥ 整体效果
  • ♠ 阶段传递数据
    • ♥ 着色器间的数据传递
    • ♥ 接口块
    • ♥ 整体效果
  • ♠ 推送
  • ♠ 结语


♠ 管线认知

按照蓝宝书第三章的内容管线按照顺序可以分为以下几个阶段:

  1. 顶点着色器
  2. 细分曲面
  3. 几何着色器
  4. 基元装配、裁剪和光栅化阶段
  5. 片段着色器
  6. 帧缓存运算
  7. 计算着色器

什么是渲染管线

简单的理解就是一堆原始图像数据会经过几个阶段最终显示在屏幕上,这几个阶段组合起来就是渲染管线,本章节将简单的去认识管线的各个组成部分


♥ 顶点着色器

我们在上一章的学习中已经简单的认识了顶点着色器,顾名思义,顶点着色器就是用来处理顶点数据的阶段,它包含了以下几个特性

  • OpenGL管线的第一个可编程阶段
  • 图形管线中唯一的必须阶段

♥ 细分曲面

这一章节对细分曲面阶段介绍的不多,我们也先做认识吧,后学的章节会继续学习

OpenGL4.0引入的新特性,细分曲面阶段是将高级基元分解为许多更小、更简单的基元进行渲染的过程,举个例子:

  • A模型由1000个三角面组成
  • 通过1000个三角面直接渲染A模型,面多顶点也多
  • 我们只导入了200个大三角面(高级基元)
  • 200个三角面在细分曲面阶段分裂成1000个面(低级基元)
  • 通过算法计算新的顶点位置

在这里插入图片描述

细分曲面主要有三部分组成,细分曲面控制着色器、固定函数细分曲面引擎、细分曲面评估着色器


♣ 细分曲面控制着色器

作为细分曲面第一阶段,控制着色器(Tessellation Control Shader)简称TCS主要是是划定了细分等级,我们写一个简单的细分曲面控制着色器

static const char * tcs_source[] =
{
    "#version 410 core                                                                 \n"
    "                                                                                  \n"
    "layout (vertices = 3) out;                                                        \n"
    "                                                                                  \n"
    "void main(void)                                                                   \n"
    "{                                                                                 \n"
    "    if (gl_InvocationID == 0)                                                     \n"
    "    {                                                                             \n"
    "        gl_TessLevelInner[0] = 5.0;                                               \n"
    "        gl_TessLevelOuter[0] = 5.0;                                               \n"
    "        gl_TessLevelOuter[1] = 5.0;                                               \n"
    "        gl_TessLevelOuter[2] = 5.0;                                               \n"
    "    }                                                                             \n"
    "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;     \n"
    "}                                                                                 \n"
};

这是官方例子tessellatedtri当中的tcs代码,我们看其中包含了哪些要点

要点1: layout (vertices = 3) out, 这里设置贴片控制点的数量为3
要点2: gl_TessLevelInner, 内部复杂度划分,gl_TessLevelInner[0] = 5.0意味着将内部纵向划分为5块
要点3: gl_TessLevelOuter, 外部复杂度划分,gl_TessLevelOuter[0] = 5.0意味着一条边被分成五部分
要点4: gl_out, 内置输出参数数组,给下一阶段用的
要点5: gl_in, 内置输入参数数组,给自己用的,包含了所有顶点信息
要点6: gl_InvocationID, 内置参数,可以作为gl_in和gl_out的索引
这里比较难理解,我们可以看看下边图方便理解,三角形每条边被分成了5分,内部纵向划成5个区域,然后再连线分成小三角形

在这里插入图片描述


♣ 固定函数细分曲面引擎

这个阶段我们不用手动编辑,主要负责生产调用细分曲面评估着色器,也就是下一阶段着色器需要的参数


♣ 细分曲面评估着色器

Tessellation Evaluation Shader,简称TES作用和顶点着色器类似,处理由固定函数细分曲面引擎传来的新的顶点,我们简单看一下代码

static const char * tes_source[] =
{
    "#version 410 core                                                                 \n"
    "                                                                                  \n"
    "layout (triangles, equal_spacing, cw) in;                                         \n"
    "                                                                                  \n"
    "void main(void)                                                                   \n"
    "{                                                                                 \n"
    "    gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) +                       \n"
    "                  (gl_TessCoord.y * gl_in[1].gl_Position) +                       \n"
    "                  (gl_TessCoord.z * gl_in[2].gl_Position);                        \n"
    "}                                                                                 \n"
};

要点1: layout (triangles, equal_spacing, cw) in, 意旨三角形模式,equal_spacing, cw等其他限定符表示应沿着多边形边缘等距、顺时针环绕生成三角形
要点2: gl_TessCoord, 新生成的顶点的重心坐标


♣ 总结

这一段内容不好理解,博主看了好久感觉也只是一知半解,后续章节再深入学习,学习期间除了蓝宝书,也受到了不少文章的启发,建议大家看看,TCS和TES的加载和顶点着色器一样,这里不再贴完整代码,需要的同学可以看官方示例tessellatedtri结合蓝宝书可以有比较清晰的认识

Vulkan_曲面细分(Tessellation Shader)

GLSL Tessellation Shader的编程入门介绍

OpenGL 4.0的Tessellation Shader(细分曲面着色器)


♥ 几何着色器

几何着色器也是一个可编程的阶段,它接收一个图元的顶点,输出的是另一组的顶点,它的作用包含但不限于以下几点:

  • 修改图元顶点位置
  • 改变输出图元(三角形变点)
  • 减少输出顶点的数量

我们看一下官方示例tessellatedgstri中的代码实例

static const char * gs_source[] =
{
    "#version 410 core                                                                  \n"
    "                                                                                   \n"
    "layout (triangles) in;                                                             \n"
    "layout (points, max_vertices = 3) out;                                             \n"
    "                                                                                   \n"
    "void main(void)                                                                    \n"
    "{                                                                                  \n"
    "    int i;                                                                         \n"
    "                                                                                   \n"
    "    for (i = 0; i < gl_in.length(); i++)                                           \n"
    "    {                                                                              \n"
    "        gl_Position = gl_in[i].gl_Position;                                        \n"
    "        EmitVertex();                                                              \n"
    "    }                                                                              \n"
    "}                                                                                  \n"
};

要点1: layout (triangles) in, 声明输入的图元类型,这里输入的是三角形,还有别的类型
要点2: layout (points, max_vertices = 3) out, 声明输出的类型,这里输出类型是点,最大顶点数是3
要点3: EmitVertex(), 内置函数,在输出中生成一个点

该段几何着色器的作用是将原来的三角形绘制改成了顶点绘制,位置不变,我们看例子运行后的效果

在这里插入图片描述


♥ 基元装配、裁剪和光栅化

此阶段就是将我们在之前处理过的一系列顶点信息转为像素的过程


♣ 基元装配

这一阶段没什么东西,基元装配指的就是将顶点转为线和三角形的过程


♣ 裁剪

顾名思义,裁剪就是抛弃掉一些不需要显示的顶点


♦ 齐次坐标

  • 百度百科
https://baike.baidu.com/item/%E9%BD%90%E6%AC%A1%E5%9D%90%E6%A0%87/511284
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);

这里先补充一个关于坐标的概念,我们所有对顶点位置的信息设置用的都是一个vec4,xyzw,三维坐标都是xyz,这种n+1的表示方式称为齐次坐标,这里的w作用是表示透视关系


♦ 标准化设备空间

OpenGL把标准坐标规定在(-1,1)之间,xyz的最大范围在-1到1之间,所有的顶点坐标会根据w的值进行投影分割,投影坐标xy在-1到1,z在0到1之间才会被显示到屏幕上,其他会被抛弃


♣ 视口转化

我们的设备分辨率是各不相同的,在裁剪后我们获得了需要显示的归一化设备坐标,将这些坐标通过比例转换和偏移,最终映射到设备坐标即为视口转化

glViewport(GLint x,GLint y,GLsizei width,GLsizei height)

我们通过内置函数glViewport来设置视口大小,xy是视口左下角位置,width和height是视口大小,一般是设备宽高

void glDepthRange(GLdouble nearVal,GLdouble farVal)

通过glDepthRange接口设置z轴的显示范围,默认是0-1,可以改成0-0.5,超过的就会被裁剪了


♣ 剔除

在进一步处理三角形前,可以选择对其进行剔除判断,三角形分背面正面,正面就是我们可以通过视口看到的位置,背面就是看不到的位置,OpenGL默认是渲染全部三角形,开启剔除后会舍弃背面三角形

glEnable(GL_CULL_FACE);

通过设置内置接口glEnable可以开启剔除功能

glCullFace(GL_FRONT);
  • GL_BACK:剔除背面
  • GL_FRONT:剔除正面
  • GL_FRONT_AND_BACK:剔除正面背面

可以通过glCullFace接口设置剔除类型


♣ 光栅化

光栅化是指哪些片段可被线或三角形等基元覆盖的过程,以下图为例

在这里插入图片描述

我们已经知道了需要绘制的几何图元(点、线、三角形),上左图的圆形

理论上一个圆形,无论怎么放大缩小都是一个理论上的圆形,但是,实际上我们屏幕显示是一个个像素格子,所以我们绘制圆到屏幕的时候需要计算以下几点:

  • 几何圆上的线和三角形所需要占用的像素格
  • 占用像素格需要填充的颜色

以上就是光栅化的过程

参考: OpenGL中着色器,渲染管线,光栅化


♥ 片段着色器

片段着色器在之前的章节我们已经有所接触,作为管线最后一个可编程的阶段,片段着色器的作用是给光栅化后所有的基元片段去着色,这里不再过多介绍,例子可以参考官方示例fragcolorfrompos,在本篇后续内容会介绍顶点着色器传递数据到片段着色器


♥ 帧缓存运算

帧缓存是OpenGL图形管线的最后一个阶段,该缓存可以表示屏幕的可见内容,以及用于存储除颜色外每个像素值的其他内存区域


♣ 像素运算

像素运算是保存为帧缓存对象前的一系列操作,包括下面几种:

  • 裁剪测试

判断像素是否丢弃

  • 模板测试

比较我们设置的参照值和模板缓存之间的大小

  • 深度测试

比较片段z值和深度缓存内容,更小的值会被渲染

  • 混合和逻辑运算

就是把这一帧的内容(还未显示)与之前的内容进行与、或、异或等计算,这部分是OpenGL的一个高度可配置的阶段,书中后续章节具体介绍


♥ 计算着色器

到这里OpenGL的渲染管线基本就结束了,计算着色器可以看做是一个独立的工作项,他不直接参与图形的渲染,只做一些非渲染的计算任务,在本书中第十章节有具体介绍,后续再讲


♠ 数据传递

第一部分内容我们学习如何向顶点着色器传递内容,并且实现在阶段之间传递数据


♥ in和out关键字

在第二章节的内容里我们认识了out关键字,out关键字可以定义输出变量,例如输出颜色给绘制的三角形,在GLSL中就是使用in和out关键字来定义输出和输入的全局变量

顶点着色器代码

"#version 420 core                                                 \n"
"                                                                  \n"
"layout (location = 0) in vec4 offset;                             \n"
"                                                                  \n"
"void main(void)                                                   \n"
"{                                                                 \n"
"    const vec4 vertices[] = vec4[](vec4( 0.25, -0.25, 0.5, 1.0),  \n"
"                                   vec4(-0.25, -0.25, 0.5, 1.0),  \n"
"                                   vec4( 0.25,  0.25, 0.5, 1.0)); \n"
"                                                                  \n"
"    // Add 'offset' to our hard-coded vertex position             \n"
"    gl_Position = vertices[gl_VertexID] + offset;                 \n"
"}                                                                 \n"

要点1: offset我们用in关键地定义了一个向量offset,在设置gl_Position的时候加上了offset
要点2: layout (location = 0)layout布局设置,location用来标注向量的位置(具体作用关联后边讲)


♥ 设置输入

在上一段内容里,我们认识了用in关键字来定义输入变量,现在我们来学习如何给输入变量赋值

我们看下面一段代码

virtual void render(double currentTime)
{
    .....

	GLfloat attrib[] = { (float)sin(currentTime) * 0.5f,
						 (float)cos(currentTime) * 0.6f,
						 0.0f, 0.0f };

	glVertexAttrib4fv(0, attrib);

    ......
}

要点1: glVertexAttrib4fv(0, attrib)我们通过glVertexAttrib*()函数来给上一段的offset来赋值,第一个参数0就对应了layout (location = 0) in vec4 offset中的0,第二个参数就是具体值了


♥ 整体效果

我们已经学习了输入变量是如何设置的,现在我们写一段完整的代码,效果就是通过设置输入变量,去移动一个三角形,具体例子参考官方示例movingtri

class singlepoint_app : public sb7::application
{
    void init()
    {
        static const char title[] = "OpenGL SuperBible - Single Point";

        sb7::application::init();

        memcpy(info.title, title, sizeof(title));
    }

    virtual void startup()
    {
        static const char * vs_source[] =
        {
            "#version 410 core                                                 \n"
            "                                                                  \n"
            "layout (location = 0) in vec4 offset;                             \n"
            "                                                                  \n"
            "void main(void)                                                   \n"
            "{                                                                 \n"
            "    const vec4 vertices[] = vec4[](vec4( 0.25, -0.25, 0.5, 1.0),  \n"
            "                                   vec4(-0.25, -0.25, 0.5, 1.0),  \n"
            "                                   vec4( 0.25,  0.25, 0.5, 1.0)); \n"
            "                                                                  \n"
            "    // Add 'offset' to our hard-coded vertex position             \n"
            "    gl_Position = vertices[gl_VertexID] + offset;                 \n"
            "}                                                                 \n"
        };

        static const char * fs_source[] =
        {
            "#version 410 core                                                 \n"
            "                                                                  \n"
            "out vec4 color;                                                   \n"
            "                                                                  \n"
            "void main(void)                                                   \n"
            "{                                                                 \n"
            "    color = vec4(0.0, 0.8, 1.0, 1.0);                             \n"
            "}                                                                 \n"
        };

        program = glCreateProgram();
        GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fs, 1, fs_source, NULL);
        glCompileShader(fs);

        GLuint vs = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vs, 1, vs_source, NULL);
        glCompileShader(vs);

        glAttachShader(program, vs);
        glAttachShader(program, fs);

        glLinkProgram(program);

        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
    }

    virtual void render(double currentTime)
    {
        static const GLfloat green[] = { 0.0f, 0.25f, 0.0f, 1.0f };
        glClearBufferfv(GL_COLOR, 0, green);

        glUseProgram(program);

        GLfloat attrib[] = { (float)sin(currentTime) * 0.5f,
                             (float)cos(currentTime) * 0.6f,
                             0.0f, 0.0f };

        glVertexAttrib4fv(0, attrib);

        glDrawArrays(GL_TRIANGLES, 0, 3);
    }

    virtual void shutdown()
    {
        glDeleteVertexArrays(1, &vao);
        glDeleteProgram(program);
    }

private:
    GLuint          program;
    GLuint          vao;
};

DECLARE_MAIN(singlepoint_app)

在这里插入图片描述


♠ 阶段传递数据

我们可以利用in和out关键字,在各着色阶段去传递变量。例如:我们可以在顶点着色器定义一个out全局变量,在片段着色器中获取他


♥ 着色器间的数据传递

  • 顶点着色器
"#version 420 core                                                 \n"
"                                                                  \n"
"layout (location = 0) in vec4 offset;                             \n"
"layout (location = 1) in vec4 color;                              \n"
"                                                                  \n"
"out vec4 vs_color;                                                \n"
"                                                                  \n"
"void main(void)                                                   \n"
"{                                                                 \n"
"    const vec4 vertices[] = vec4[](vec4( 0.25, -0.25, 0.5, 1.0),  \n"
"                                   vec4(-0.25, -0.25, 0.5, 1.0),  \n"
"                                   vec4( 0.25,  0.25, 0.5, 1.0)); \n"
"                                                                  \n"
"    // Add 'offset' to our hard-coded vertex position             \n"
"    gl_Position = vertices[gl_VertexID] + offset;                 \n"
"    vs_color = color;										       \n"
"}                                                                 \n"

要点1: vs_color我们定义了输出变量vs_color并且,给vs_color赋值color是我们的一个外部输入值

  • 片段着色器
"#version 420 core                             \n"
"                                              \n"
"in vec4 vs_color;                             \n"
"                                              \n"
"out vec4 color;                               \n"
"                                              \n"
"void main(void)                               \n"
"{                                             \n"
"    color = vs_color;                         \n"
"}                                             \n"

要点1: vs_color我们定义了和顶点着色器当中相同命名的一个输入变量,如此我们在片段着色器中就可以获取到顶点着色器当中定义的变量了


♥ 接口块

除去直接用in和out关键字定义的数据,针对可能存在的大量数据,我们还可以通过定义接口块来进行输入和输出变量

  • 顶点着色器
"#version 420 core                                                 \n"
"                                                                  \n"
"layout (location = 0) in vec4 offset;                             \n"
"layout (location = 1) in vec4 color;                              \n"
"                                                                  \n"
"out VS_OUT                                                         \n"
"{                                                                  \n"
"    vec4 color;                                                    \n"
"}vs_out;                                                          \n"
"                                                                  \n"
"void main(void)                                                   \n"
"{                                                                 \n"
"    const vec4 vertices[] = vec4[](vec4( 0.25, -0.25, 0.5, 1.0),  \n"
"                                   vec4(-0.25, -0.25, 0.5, 1.0),  \n"
"                                   vec4( 0.25,  0.25, 0.5, 1.0)); \n"
"                                                                  \n"
"    // Add 'offset' to our hard-coded vertex position             \n"
"    gl_Position = vertices[gl_VertexID] + offset;                 \n"
"    vs_out.color = color;										   \n"
"}                                                                 \n"

要点1: 我们在顶点着色器中用out关键字定义了一个VS_OUT结构,并定义了一个实例vs_out,目前结构体只有一个color变量

  • 片段着色器
"#version 420 core                             \n"
"                                              \n"
"in VS_OUT                                    \n"
"{                                             \n"
"    vec4 color;                               \n"
"}fs_in;                                       \n"
"                                              \n"
"out vec4 color;                               \n"
"                                              \n"
"void main(void)                               \n"
"{                                             \n"
"    color = fs_in.color;                      \n"
"}                                             \n"

要点1: 我们在片段着色器中用in关键字定义了一个和顶点着色器同名的VS_OUT结构,并定义了一个实例fs_out,结构体参数和顶点着色器保持一致
要点2: 我们可以通过同名的接口快VS_OUT实现阶段内数据的传递
要点3: 实例vs_outfs_in为了区分,以免出现混淆


♥ 整体效果

下面是整体代码和效果,效果和第一阶段类似,加了一个在移动时三角形随时间变色的逻辑

在这里插入图片描述

#include <sb7.h>

class test3_OpenGL : public sb7::application
{

	virtual void startup()
	{
		program = compile_shaders();
		glGenVertexArrays(1, &vao);
		glBindVertexArray(vao);
	}
	virtual void render(double currentTime)
	{
		static const GLfloat green[] = { 0.0f, 0.25f, 0.0f, 1.0f };
		glClearBufferfv(GL_COLOR, 0, green);

		glUseProgram(program);

		GLfloat attrib[] = { (float)sin(currentTime) * 0.5f,
							 (float)cos(currentTime) * 0.6f,
							 0.0f, 0.0f };

		glVertexAttrib4fv(0, attrib);

		GLfloat sColor[] = { (float)sin(currentTime),(float)cos(currentTime),0.0f, 1.0f };

		glVertexAttrib4fv(1, sColor);


		glDrawArrays(GL_TRIANGLES, 0, 3);
	}

	/
	// func:编写一个简单的着色器
	/
	GLuint compile_shaders(void)
	{
		GLuint vertex_shader;
		GLuint fragment_shader;
		GLuint program;

		//顶点着色器
		static const char * vs_source[] =
		{
			"#version 420 core                                                 \n"
			"                                                                  \n"
			"layout (location = 0) in vec4 offset;                             \n"
			"layout (location = 1) in vec4 color;                              \n"
			"                                                                  \n"
			"out VS_OUT                                                         \n"
			"{                                                                  \n"
			"    vec4 color;                                                    \n"
			"}vs_out;                                                          \n"
			"                                                                  \n"
			"void main(void)                                                   \n"
			"{                                                                 \n"
			"    const vec4 vertices[] = vec4[](vec4( 0.25, -0.25, 0.5, 1.0),  \n"
			"                                   vec4(-0.25, -0.25, 0.5, 1.0),  \n"
			"                                   vec4( 0.25,  0.25, 0.5, 1.0)); \n"
			"                                                                  \n"
			"    // Add 'offset' to our hard-coded vertex position             \n"
			"    gl_Position = vertices[gl_VertexID] + offset;                 \n"
			"    vs_out.color = color;										   \n"
			"}                                                                 \n"



		};
		//片段着色器
		static const char * fs_source[] =
		{
			"#version 420 core                             \n"
			"                                              \n"
			"in VS_OUT                                    \n"
			"{                                             \n"
			"    vec4 color;                               \n"
			"}fs_in;                                       \n"
			"                                              \n"
			"out vec4 color;                               \n"
			"                                              \n"
			"void main(void)                               \n"
			"{                                             \n"
			"    color = fs_in.color;                      \n"
			"}                                             \n"
		};

		vertex_shader = glCreateShader(GL_VERTEX_SHADER);
		glShaderSource(vertex_shader, 1, vs_source, NULL);
		glCompileShader(vertex_shader);

		fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
		glShaderSource(fragment_shader, 1, fs_source, NULL);
		glCompileShader(fragment_shader);


		program = glCreateProgram();
		glAttachShader(program, vertex_shader);
		glAttachShader(program, fragment_shader);
		glLinkProgram(program);

		glDeleteShader(vertex_shader);
		glDeleteShader(fragment_shader);

		return program;
	}


	void shutdown()
	{
		glDeleteVertexArrays(1, &vao);
		glDeleteProgram(program);
	}

private:
	GLuint          program;
	GLuint          vao;

};

DECLARE_MAIN(test3_OpenGL)



♠ 推送

  • Github
https://github.com/KingSun5

♠ 结语

本章东西比较多,但也只是对管线各阶段的初步认识,后续章节会更加细致的去学习,若是觉得博主的文章写的不错,不妨关注一下博主,点赞一下博文,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。
本文属于原创文章,转载请著名作者出处并置顶!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/145313.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【KG】TransE 及其实现

原文&#xff1a;https://yubincloud.github.io/notebook/pages/paper/kg/TransE/ TransE 及其实现 1. What is TransE? TransE (Translating Embedding), an energy-based model for learning low-dimensional embeddings of entities. 核心思想&#xff1a;将 relationship …

基于R的Bilibili视频数据建模及分析——建模-因子分析篇

基于R的Bilibili视频数据建模及分析——建模-因子分析篇 文章目录基于R的Bilibili视频数据建模及分析——建模-因子分析篇0、写在前面1、数据分析1.1 建模-因子分析1.2 对数线性模型1.3 主成分分析1.4 因子分析1.5 多维标度法2、参考资料0、写在前面 实验环境 Python版本&#…

防火墙命令

启动&#xff1a; systemctl start firewalld 查看状态&#xff1a; systemctl status firewalld 停止&#xff1a;systemctl stop firewalld 禁用&#xff1a;systemctl disable firewalld 怎么开启一个端口呢 添加 firewall-cmd --zonepublic --add-port80/tcp --permanent …

easyx保姆级教程---->从游戏玩家到游戏制作者

请点击这里&#xff1a;安装教程 1.头文件 #include<easyx.h> //这个是只包含最新的API(函数接口) #include<graphics.h> //这个头文件包含了上面的&#xff0c;还包含了已经不推荐使用的函数2.窗口 1.初始化绘制窗口 initgraph(width,height,flag); //窗…

Domino Web应用中的搜索功能和结果选择问题

大家好&#xff0c;才是真的好。 还有不到十天Domino多瑙河版本就将发布&#xff0c;在此之前&#xff0c;我们还是讲述一下Web中的搜索技术。 废话不多说&#xff0c;我们直接上干货。 Notes应用的视图在Web浏览器中可以直接展现&#xff0c;并且可选择。 如果这样展现的话…

【QGIS入门实战精品教程】8.1:QGIS制作地图案例教程

文章目录 一、加载矢量数据二、加载影像底图三、美化矢量数据四、切换到排版视图五、添加经纬度格网六、添加其他修饰元素七、地图输出一、加载矢量数据 加载本实验数据基础数据.gpkg中的甘肃省政区矢量数据,如下所示: 二、加载影像底图 QGIS加载在线地图案例教程参考: 【…

5、Java中的JDBCJDBCUtilsJDBC控制事务getResource中文或有空格路径处理ResourceBundle演示

JDBC&#xff1a; 1. 概念&#xff1a;Java DataBase Connectivity Java 数据库连接&#xff0c; Java语言操作数据库 * JDBC本质&#xff1a;其实是官方&#xff08;sun公司&#xff09;定义的一套操作所有关系型数据库的规则&#xff0c;即接口。各个数据库厂商去实现这…

回收租赁商城系统功能拆解04讲-商品品牌

回收租赁系统适用于物品回收、物品租赁、二手买卖交易等三大场景。 可以快速帮助企业搭建类似闲鱼回收/爱回收/爱租机/人人租等回收租赁商城。 回收租赁系统支持智能评估回收价格&#xff0c;后台调整最终回收价&#xff0c;用户同意回收后系统即刻放款&#xff0c;用户微信零…

第04章 程序控制结构

在程序中&#xff0c;程序运行的流程控制决定程序是如何执行的。 顺序控制 介绍&#xff1a; 程序从上到下的逐行的执行&#xff0c;中间没有任何判断和跳转。 使用&#xff1a;java中定义变量时&#xff0c;采用合法的前向引用。如&#xff1a; public class Test{int num…

【虚幻引擎】UE4/UE5像素流在广域网上(云)部署(多实例)

一、选择云服务器 每个云平台都提供许多预设的镜像选择&#xff0c;由于像素流技术目前只支持Windows操作系统&#xff0c;所以我们需要选择Windows Server的镜像&#xff0c;2012/2016/2019皆可。我们这里选择了Windows Server 2016 R2 简体中文版的镜像&#xff0c;之所以选择…

【SSM整合】对Spring、SpringMVC、MyBatis的整合,以及Bootstrap的使用,简单的新闻管理系统

✅作者简介&#xff1a;热爱Java后端开发的一名学习者&#xff0c;大家可以跟我一起讨论各种问题喔。 &#x1f34e;个人主页&#xff1a;Hhzzy99 &#x1f34a;个人信条&#xff1a;坚持就是胜利&#xff01; &#x1f49e;当前专栏&#xff1a;【Spring】 &#x1f96d;本文内…

代码随想录第53天|● 1143.最长公共子序列 ● 1035.不相交的线 ● 53. 最大子序和 动态规划

1143.最长公共子序列 和718.最长重复子数组类似 包括二维数组初始化这些 不同之处在于递推公式主要就是两大情况&#xff1a; text1[i - 1] 与 text2[j - 1]相同&#xff0c;text1[i - 1] 与 text2[j - 1]不相同 如果text1[i - 1] 与 text2[j - 1]相同&#xff0c;那么找到了…

Windows/Linux日志分析

Windows日志分析 Windows系统日志是记录系统中硬件、软件和系统问题的信息&#xff0c;同时还可以监视系统中发生的事件。用户可以通过它来检查错误发生的原因&#xff0c;或者寻找受到攻击时攻击者留下的痕迹。 Windows主要有以下三类日志记录系统事件&#xff1a;应用程序日志…

【链表】leetcode707.设计链表(C/C++/Java/Js)

leetcode707.设计链表1 题目2 思路3 代码3.1 C版本3.2 C版本3.3 Java版本3.3.1 单链表3.3.2 双链表3.4 JavaScript版本4 总结1 题目 题源链接 设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性&#xff1a;val 和 next。val 是当前节点的值&…

2022年地图产业研究报告

第一章 行业概况 地图是按照一定法则&#xff0c;有选择地以二维或多维形式与手段在平面或球面上表示地球&#xff08;或其它星球&#xff09;若干现象的图形或图像&#xff0c;它具有严格的数学基础、符号系统、文字注记&#xff0c;并能用地图概括原则&#xff0c;科学地反映…

canvasjs javascript-charts 3.7.3 Crack

canvasjs javascript-charts/ 3.7.3 具有 30 多种图表类型的 JavaScript 图表库 具有 10 倍性能和 30 多种图表类型的 JavaScript 图表和图形库。核心 JavaScript 图表库是独立的&#xff0c;但也带有流行框架的组件&#xff0c;如 React、Angular、Vue 等。图表响应迅速&#…

14、RH850 F1 RAM存储器介绍

前言: RAM——程序运行中数据的随机存取&#xff08;掉电后数据消失&#xff09;整个程序中&#xff0c;所用到的需要被改写的量&#xff0c;都存储在RAM中&#xff0c;“被改变的量”包括全局变量、局部变量、堆栈段&#xff0c;此专栏会有针对SPI的工作原理的详细介绍。 一、…

性能优化系列之如何选择合适的WebView内核?

文章の目录一、iOS UIWebView1、优点2、不足二、iOS WKWebView1、优势2、不足三、Android WebKit 和 Chromium四、Android 第三方1、X5 内核五、选型建议写在最后一、iOS UIWebView 1、优点 从 iOS 2 开始就作为 App 内展示 Web 内容的容器排版布局能力强 2、不足 内存泄露…

将两个对象以指定方法按指定轴对齐的DataFrame.align()方法

【小白从小学Python、C、Java】【计算机等级考试500强双证书】【Python-数据分析】将两个对象以指定方法按指定轴对齐DataFrame.align()选择题关于以下python代码说法错误的一项是?import pandas as pddf1 pd.DataFrame({"A": [1,2],"B":[3,4]})df2 pd.…

MySQL延时关联使查询速度提升N倍

以下内容也可以观看视频教程&#xff1a; https://space.bilibili.com/431152063先来看下面的sql语句&#xff1a; select * from orderinfo limit 1000000, 100目前orderinfo表中的数据大概是1亿行 查询耗时大概2秒多&#xff0c;如果将sql中的返回所有字段改成只返回dbid字段…