Qt5版NeHe OpenGL教程之七:混色

2023-10-18 21:40

本文主要是介绍Qt5版NeHe OpenGL教程之七:混色,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简单的透明
       OpenGL中的绝大多数特效都与某些类型的(色彩)混合有关。混色的定义为,将某个象素的颜色和已绘制在屏幕上与其对应的象素颜色相互结合。至于如何结合这两个颜色则依赖于颜色的alpha通道的分量值,以及/或者所使用的混色函数。Alpha通常是位于颜色值末尾的第4个颜色组成分量。前面这些课我们都是用GL_RGB来指定颜色的三个分量。相应的GL_RGBA可以指定alpha分量的值。更进一步,我们可以使用glColor4f()来代替glColor3f()。
       绝大多数人都认为Alpha分量代表材料的透明度。这就是说,alpha值为0.0时所代表的材料是完全透明的。alpha值为1.0时所代表的材料则是完全不透明的。 
混色的概念

       所谓混色,就是将当前要绘制的物体的颜色和颜色缓冲区中已经绘制了的物体的颜色进行混合,最终决定了当前物体的颜色。例如下面的图中,狙击枪的瞄准器本身是带有蓝色的,将它和后面的任务混合在一起,形成了我们看到的最终效果,这个效果里既有瞄准器的蓝色成分,也有后面人物的像素,主要是后面人物的像素。


OpenGL中的混色
       正确的混色过程应该是先绘制全部的场景之后再绘制透明的图形。并且要按照与深度缓存相反的次序来绘制(先画最远的物体)。
       考虑对两个多边形(1和2)进行alpha混合,不同的绘制次序会得到不同的结果。(这里假定多边形1离观察者最近,那么正确的过程应该先画多边形2,再画多边形1。正如您再现实中所见到的那样,从这两个透明的多边形背后照射来的光线总是先穿过多边形2,再穿过多边形1,最后才到达观察者的眼睛。)
       在深度缓存启用时,您应该将透明图形按照深度进行排序,并在全部场景绘制完毕之后再绘制这些透明物体。否则您将得到不正确的结果。我知道某些时候这样做是很令人痛苦的,但这是正确的方法。
       混色后可以通过当前物体看到其后的物体,这里当前物体的最终颜色是由当前物体的颜色(源的颜色 source color)和颜色缓冲区中的颜色(目的颜色 destination color)混色决定的,也就是进行相应的混合计算得到的。
       要开启混色功能需要使用:
glEnable(GL_BLEND);
       混色是计算出来的,主体的公式是这样的:
Result=source∗sfactor+destination∗dfactor     (1)
       公式中source和destination表示的分别是源和目的颜色,先绘制的将成为“目的颜色”,后绘制的将成为“源颜色”,sFactor 和dFactor分别表示源和目的颜色的计算系数。 用户可以灵活的控制公式中的sFactor和dFactor,上式计算是逐个颜色分量RGBA计算的。OpenGL提供了函数glBlendFunc用来设置上面的sfactor和dfactor,函数原型为:
API void glBlendFunc( GLenum sfactor, GLenum dfactor); 
    sfactor和dfactor用来指定源和目的颜色计算的系数,使用的是GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR等枚举值。

       例如,一个红色和绿色方块进行混色,效果如下图所示:


       这里绿色(0.0,1.0,0.0,0.6)作为源,红色(1.0,0.0,0.0,1.0)作为目的颜色进行混合。我们设置参数为:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);  
       则进行计算的过程为:
result=(0.0,1.0,0.0,0.6)∗(0.6,0.6,0.6,0.6)+(1.0,0.0,0.0,1.0)∗(0.4,0.4,0.4,0.4)=(0.4,0.6,0.0,0.76)
       除了glBlendFunc外,还可以使用使用glBlendFuncSeparate单独指定RGB,Alpha的计算系数。
API void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); 
       这里的参数同样是GL_ZERO,GL_ONE,GL_SRC_COLOR等枚举值。
       另外,还可以通过glBlendEquation(GLenum mode);和glBlendEquationSeparate来指定源和目的颜色的计算方式,默认是GL_FUNC_ADD,就是公式1所示的情况。例如GL_FUNC_SUBTRACT则对应公式2:
Result=source∗sfactor−destination∗dfactor     (2)
       一般我们使用的组合为:
glBlendEquation(GL_FUNC_ADD); // 默认,无需显式设置
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);  

混合3D物体
混合3D物体时,基本原理和混合2D物体一样,但需要将深度检测关闭或设置为只读。因为深度检测会剔除被遮挡的部分物体。

glEnable( GL_DEPTH_TEST );          // 启用深度缓存
glDisable( GL_DEPTH_TEST );         // 禁用深度缓存glDepthMask( GL_FALSE );            // 深度缓存为 只读
glDepthMask( GL_TRUE );             // 深度缓存为 读/写

源码

lesson7.h

#ifndef LESSON7_H
#define LESSON7_H#include <QWindow>
#include <QOpenGLFunctions_1_1>
#include <QKeyEvent>class QPainter;
class QOpenGLContext;
class QOpenGLPaintDevice;class Lesson7 : public QWindow, QOpenGLFunctions_1_1
{Q_OBJECT
public:explicit Lesson7(QWindow *parent = 0);~Lesson7();virtual void render(QPainter *);virtual void render();virtual void initialize();public slots:void renderNow();protected:void exposeEvent(QExposeEvent *);void resizeEvent(QResizeEvent *);void keyPressEvent(QKeyEvent *); // 键盘事件private:void loadGLTexture();private:QOpenGLContext *m_context;bool light;         // 点击“L”键开关光源bool blend;         // 是否开启混合GLfloat	xrot;		// X 旋转GLfloat	yrot;		// Y 旋转GLfloat xspeed;	    // X 旋转速度GLfloat yspeed;		// Y 旋转速度GLfloat	z;	        // 深入屏幕的距离GLfloat *LightAmbient;  // 环境光参数GLfloat *LightDiffuse;  // 漫射光参数GLfloat *LightPosition; // 光源位置GLuint filter;		// 滤波类型GLuint texture[3];	// 3种纹理的储存空间
};#endif // LESSON7_H

lesson7.cpp

#include "lesson7.h"#include <QCoreApplication>
#include <QOpenGLContext>
#include <QOpenGLPaintDevice>
#include <QPainter>
#include <QDebug>
#include <GL/GLU.h>Lesson7::Lesson7(QWindow *parent) :QWindow(parent), m_context(0)
{setSurfaceType(QWindow::OpenGLSurface);light=false;blend=false;xrot=45.0f;yrot=45.0f;xspeed=0.0f;yspeed=0.0f;z=-5.0f;filter=0;LightAmbient=new GLfloat[4]{ 0.5f, 0.5f, 0.5f, 1.0f };LightDiffuse=new GLfloat[4]{ 1.0f, 1.0f, 1.0f, 1.0f };LightPosition=new GLfloat[4]{ 0.0f, 0.0f, 2.0f, 1.0f };
}Lesson7::~Lesson7()
{glDeleteTextures(3, &texture[0]);
}void Lesson7::render(QPainter *painter)
{Q_UNUSED(painter);
}void Lesson7::render()
{glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);glViewport(0,0,(GLint)width(),(GLint)height()); // 重置当前视口glMatrixMode(GL_PROJECTION);                    // 选择投影矩阵glLoadIdentity();                               // 重置投影矩阵为单位矩阵gluPerspective(45.0f,(GLdouble)width()/(GLdouble)height(),0.1f,100.0f);glMatrixMode(GL_MODELVIEW);// 选择模型视图矩阵glLoadIdentity();          // 重置模型视图矩阵为单位矩阵glTranslatef(0.0f,0.0f,z);  // 移入屏幕z个单位//下面两行使立方体绕X、Y轴旋转。旋转多少依赖于变量xrot和yrot的值。glRotatef(xrot,1.0f,0.0f,0.0f); // X轴旋转glRotatef(yrot,0.0f,1.0f,0.0f); // Y轴旋转//下一行与我们在第五课中的类似。有所不同的是,这次我们绑定的纹理是texture[filter],而不是上一课中的texture[0]。//任何时候,我们按下F键,filter 的值就会增加。如果这个数值大于2,变量filter 将被重置为0。//程序初始时,变量filter 的值也将设为0。使用变量filter 我们就可以选择三种纹理中的任意一种。glBindTexture(GL_TEXTURE_2D, texture[filter]); // 选择由filter决定的纹理//为了将纹理正确的映射到四边形上,您必须将纹理的右上角映射到四边形的右上角,纹理的左上角映射到四边形的左上角,//纹理的右下角映射到四边形的右下角,纹理的左下角映射到四边形的左下角。//如果映射错误的话,图像显示时可能上下颠倒,侧向一边或者什么都不是。//glTexCoord2f 的第一个参数是X坐标。 0.0f 是纹理的左侧。 0.5f 是纹理的中点, 1.0f 是纹理的右侧。//glTexCoord2f 的第二个参数是Y坐标。 0.0f 是纹理的底部。 0.5f 是纹理的中点, 1.0f 是纹理的顶部。//所以纹理的左上坐标是 X:0.0f,Y:1.0f ,四边形的左上顶点是 X: -1.0f,Y:1.0f 。其余三点依此类推。//试着玩玩 glTexCoord2f X, Y坐标参数。把 1.0f 改为 0.5f 将只显示纹理的左半部分,把 0.0f 改为 0.5f 将只显示纹理的右半部分。//glNormal3f是这一课的新东西。Normal就是法线的意思,所谓法线是指经过面(多边形)上的一点且垂直于这个面(多边形)的直线。//使用光源的时候必须指定一条法线。法线告诉OpenGL这个多边形的朝向,并指明多边形的正面和背面。//如果没有指定法线,什么怪事情都可能发生:不该照亮的面被照亮了,多边形的背面也被照亮....。对了,法线应该指向多边形的外侧。//看着木箱的前面您会注意到法线与Z轴正向同向。这意味着法线正指向观察者-您自己。这正是我们所希望的。//对于木箱的背面,也正如我们所要的,法线背对着观察者。如果立方体沿着X或Y轴转个180度的话,//前侧面的法线仍然朝着观察者,背面的法线也还是背对着观察者。换句话说,不管是哪个面,只要它朝着观察者这个面的法线就指向观察者。//由于光源紧邻观察者,任何时候法线对着观察者时,这个面就会被照亮。并且法线越朝着光源,就显得越亮一些。//如果您把观察点放到立方体内部,你就会法线里面一片漆黑。因为法线是向外指的。如果立方体内部没有光源的话,当然是一片漆黑。glBegin(GL_QUADS);// 前面glNormal3f( 0.0f, 0.0f, 1.0f);					            // 法线指向观察者glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// 纹理和四边形的左下glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// 纹理和四边形的右下glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// 纹理和四边形的右上glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// 纹理和四边形的左上// 后面glNormal3f( 0.0f, 0.0f,-1.0f);					            // 法线背向观察者glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// 纹理和四边形的右下glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// 纹理和四边形的右上glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// 纹理和四边形的左上glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// 纹理和四边形的左下// 顶面glNormal3f( 0.0f, 1.0f, 0.0f);					            // 法线向上glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// 纹理和四边形的左上glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// 纹理和四边形的左下glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// 纹理和四边形的右下glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// 纹理和四边形的右上// 底面glNormal3f( 0.0f,-1.0f, 0.0f);					            // 法线朝下glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// 纹理和四边形的右上glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// 纹理和四边形的左上glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// 纹理和四边形的左下glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// 纹理和四边形的右下// 右面glNormal3f( 1.0f, 0.0f, 0.0f);			                    // 法线朝右glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// 纹理和四边形的右下glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// 纹理和四边形的右上glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// 纹理和四边形的左上glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// 纹理和四边形的左下// 左面glNormal3f(-1.0f, 0.0f, 0.0f);					            // 法线朝左glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// 纹理和四边形的左下glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// 纹理和四边形的右下glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// 纹理和四边形的右上glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// 纹理和四边形的左上glEnd();
}void Lesson7::initialize()
{loadGLTexture();                      // 加载纹理glEnable(GL_TEXTURE_2D);              // 启用纹理映射glShadeModel(GL_SMOOTH);              // 启用平滑着色glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 黑色背景glClearDepth(1.0f);                   // 设置深度缓存glEnable(GL_DEPTH_TEST);              // 启用深度测试glDepthFunc(GL_LEQUAL);               // 深度测试类型// 接着告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// 现在开始设置光源。glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);	  // 设置环境光glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);	  // 设置漫射光glLightfv(GL_LIGHT1, GL_POSITION, LightPosition); // 设置光源位置// 最后,我们启用一号光源。// 记住:只对光源进行设置、定位,光源都不会工作。除非我们启用GL_LIGHTING。glEnable(GL_LIGHT1);							  // 启用一号光源if(!light){glDisable(GL_LIGHTING);		// 禁用光源}else{glEnable(GL_LIGHTING);		// 启用光源}// 加入以下两行。第一行以全亮度绘制此物体,并对其进行50%的alpha混合(半透明)。// 当混合选项打开时,此物体将会产生50%的透明效果。第二行设置所采用的混合类型。// 注意:alpha通道的值为0.0意味着物体材质是完全透明的。1.0则意味着完全不透明。glColor4f(1.0f,1.0f,1.0f,0.5f);		// 全亮度, 50% Alpha 混合glBlendFunc(GL_SRC_ALPHA,GL_ONE);	// 基于源象素alpha通道值的半透明混合函数if(blend){glEnable(GL_BLEND);		        // 打开混合glDisable(GL_DEPTH_TEST);	    // 关闭深度测试}else{glDisable(GL_BLEND);		    // 关闭混合glEnable(GL_DEPTH_TEST);	    // 打开深度测试}
}void Lesson7::renderNow()
{if (!isExposed())return;bool needsInitialize = false;if (!m_context) {m_context = new QOpenGLContext(this);m_context->setFormat(requestedFormat());m_context->create();needsInitialize = true;}m_context->makeCurrent(this);if (needsInitialize) {initializeOpenGLFunctions();initialize();}render();m_context->swapBuffers(this);
}
//filter 变量跟踪显示时所采用的纹理类型。第一种纹理(texture 0) 使用gl_nearest(近邻滤波)方式构建。
//第二种纹理 (texture 1) 使用gl_linear(线性滤波) 方式,离屏幕越近的图像看起来就越光滑。
//第三种纹理 (texture 2) 使用 mipmapped滤波方式,这将创建一个外观十分优秀的纹理。
//根据我们的使用类型,filter 变量的值分别等于 0, 1 或 2 。下面我们从第一种纹理开始。
//GLuint texture[3] 为三种不同纹理分配储存空间。它们分别位于在 texture[0], texture[1] 和 texture[2]中。
void Lesson7::loadGLTexture()
{// 现在载入图像,并将其转换为纹理。QImage image(":/image/Glass.bmp");image = image.convertToFormat(QImage::Format_RGB888);image = image.mirrored();// 创建纹理glGenTextures(3, &texture[0]);// 第五课中我们使用了线性滤波的纹理贴图。这需要机器有相当高的处理能力,但它们看起来很不错。// 这一课中,我们接着要创建的第一种纹理使用 GL_NEAREST 方式。从原理上讲,这种方式没有真正进行滤波。// 它只占用很小的处理能力,看起来也很差。唯一的好处是这样我们的工程在很快和很慢的机器上都可以正常运行。// 您会注意到我们在 MIN 和 MAG 时都采用了GL_NEAREST,你可以混合使用 GL_NEAREST 和 GL_LINEAR。// 创建 Nearest 滤波贴图glBindTexture(GL_TEXTURE_2D, texture[0]);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,image.width(),image.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image.bits());// 创建线性滤波纹理glBindTexture(GL_TEXTURE_2D, texture[1]);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,image.width(),image.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image.bits());// 下面是创建纹理的新方法。 Mipmapping!『译者注:这个词的中文我翻不出来,不过没关系。看完这一段,您就知道意思最重要。』// 您可能会注意到当图像在屏幕上变得很小的时候,很多细节将会丢失。刚才还很不错的图案变得很难看。// 当您告诉OpenGL创建一个mipmapped的纹理后,OpenGL将尝试创建不同尺寸的高质量纹理。当您向屏幕绘制一个mipmapped纹理的时候,// OpenGL将选择它已经创建的外观最佳的纹理(带有更多细节)来绘制,而不仅仅是缩放原先的图像(这将导致细节丢失)。// 我曾经说过有办法可以绕过OpenGL对纹理宽度和高度所加的限制——64、128、256,等等。// 办法就是gluBuild2DMipmaps。据我的发现,您可以使用任意的位图来创建纹理。OpenGL将自动将它缩放到正常的大小。// 因为是第三个纹理,我们将它存到texture[2]。这样本课中的三个纹理全都创建好了。// 创建 MipMapped 纹理glBindTexture(GL_TEXTURE_2D, texture[2]);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, image.width(),image.width(), GL_RGB, GL_UNSIGNED_BYTE, image.bits());
}void Lesson7::exposeEvent(QExposeEvent *event)
{Q_UNUSED(event);if (isExposed()){renderNow();}
}void Lesson7::resizeEvent(QResizeEvent *event)
{Q_UNUSED(event);if (isExposed()){renderNow();}
}void Lesson7::keyPressEvent(QKeyEvent *event)
{int key=event->key();switch(key){case Qt::Key_L:{light = !light;if(!light){glDisable(GL_LIGHTING);	// 禁用光源}else{glEnable(GL_LIGHTING);	// 启用光源}break;}case Qt::Key_F:{filter+=1;if(filter > 2){filter = 0;}break;}case Qt::Key_B:{blend = !blend;				    // 切换混合选项的 TRUE / FALSEif(blend){glEnable(GL_BLEND);		    // 打开混合glDisable(GL_DEPTH_TEST);	// 关闭深度测试}else{glDisable(GL_BLEND);		// 关闭混合glEnable(GL_DEPTH_TEST);	// 打开深度测试}break;}case Qt::Key_PageUp:{z-=1.0f;break;}case Qt::Key_PageDown:{z+=1.0f;break;}case Qt::Key_Up:{xspeed=-1.0f;xrot+=xspeed;break;}case Qt::Key_Down:{xspeed=1.0f;xrot+=xspeed;break;}case Qt::Key_Right:{yspeed=1.0f;yrot+=yspeed;break;}case Qt::Key_Left:{yspeed=-1.0f;yrot+=yspeed;break;}}if(key==Qt::Key_L||key==Qt::Key_F||key==Qt::Key_B||key==Qt::Key_PageUp||key==Qt::Key_PageDown||key==Qt::Key_Up||key==Qt::Key_Down||key==Qt::Key_Right||key==Qt::Key_Left){renderNow();}
}

main.cpp

#include <QGuiApplication>
#include <lesson7.h>
int main(int argc, char *argv[])
{QGuiApplication app(argc, argv);QSurfaceFormat format;format.setSamples(16);Lesson7 window;window.setFormat(format);window.resize(640, 480);window.show();return app.exec();
}

运行效果


按键控制

B键:打开和关闭混色


Qt5版本NeHe OpenGL教程6-10课源码链接:https://download.csdn.net/download/caoshangpa/10420544


这篇关于Qt5版NeHe OpenGL教程之七:混色的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/235304

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

沁恒CH32在MounRiver Studio上环境配置以及使用详细教程

目录 1.  RISC-V简介 2.  CPU架构现状 3.  MounRiver Studio软件下载 4.  MounRiver Studio软件安装 5.  MounRiver Studio软件介绍 6.  创建工程 7.  编译代码 1.  RISC-V简介         RISC就是精简指令集计算机(Reduced Instruction SetCom

前端技术(七)——less 教程

一、less简介 1. less是什么? less是一种动态样式语言,属于css预处理器的范畴,它扩展了CSS语言,增加了变量、Mixin、函数等特性,使CSS 更易维护和扩展LESS 既可以在 客户端 上运行 ,也可以借助Node.js在服务端运行。 less的中文官网:https://lesscss.cn/ 2. less编译工具 koala 官网 http://koala-app.

【Shiro】Shiro 的学习教程(三)之 SpringBoot 集成 Shiro

目录 1、环境准备2、引入 Shiro3、实现认证、退出3.1、使用死数据实现3.2、引入数据库,添加注册功能后端代码前端代码 3.3、MD5、Salt 的认证流程 4.、实现授权4.1、基于角色授权4.2、基于资源授权 5、引入缓存5.1、EhCache 实现缓存5.2、集成 Redis 实现 Shiro 缓存 1、环境准备 新建一个 SpringBoot 工程,引入依赖:

Windows环境利用VS2022编译 libvpx 源码教程

libvpx libvpx 是一个开源的视频编码库,由 WebM 项目开发和维护,专门用于 VP8 和 VP9 视频编码格式的编解码处理。它支持高质量的视频压缩,广泛应用于视频会议、在线教育、视频直播服务等多种场景中。libvpx 的特点包括跨平台兼容性、硬件加速支持以及灵活的接口设计,使其可以轻松集成到各种应用程序中。 libvpx 的安装和配置过程相对简单,用户可以从官方网站下载源代码

PHP APC缓存函数使用教程

APC,全称是Alternative PHP Cache,官方翻译叫”可选PHP缓存”。它为我们提供了缓存和优化PHP的中间代码的框架。 APC的缓存分两部分:系统缓存和用户数据缓存。(Linux APC扩展安装) 系统缓存 它是指APC把PHP文件源码的编译结果缓存起来,然后在每次调用时先对比时间标记。如果未过期,则使用缓存的中间代码运行。默认缓存 3600s(一小时)。但是这样仍会浪费大量C

Qt多语种开发教程

Qt作为跨平台的开发工具,早已应用到各行各业的软件开发中。 今天讲讲,Qt开发的正序怎么做多语言开发。就是说,你设置中文,就中文显示;设置英语就英文显示,设置繁体就繁体显示,设置发育就显示法语等。 开发环境(其实多语种这块根环境没太大关系):win10,Qt.5.12.10 一.先用QtCreator创建一个简单的桌面程序 1.工程就随便命名“LanguageTest”,其他默认。 2.在设计师

如何打造个性化大学生线上聊天交友系统?Java SpringBoot Vue教程,2025最新设计思路

✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 | SpringBoot/SSM Python实战项目 | Django 微信小程序/安卓实战项目 大数据实战项目 ⚡⚡文末获取源码 文章目录