OpenGL ES之四——绘制点,线,三角形_gldisablevertexattribarray-程序员宅基地

技术标签: AndroidStudio中配置GLSL环境  OpenGL ES  AndroidStudiio中GLSL插件  OpenGL ES简单使用  

概述

这是一个系列的Android平台下OpenGl ES介绍,从最基本的使用最终到VR图的展示的实现,属于基础篇。(后面针对VR视频会再有几篇文章,属于进阶篇)

OpenGL ES之一——概念扫盲
OpenGL ES之二——Android中的OpenGL ES概述
OpenGL ES之三——绘制纯色背景
OpenGL ES之四——绘制点,线,三角形
OpenGL ES之五——相机和投影,绘制等腰三角形
OpenGL ES之六——绘制矩形和圆形
OpenGL ES之七——着色器语言GLSL
OpenGL ES之八——GLES20类和Matrix类
OpenGL ES之九——相机和投影
OpenGL ES之十——纹理贴图(展示一张图片)
OpenGL ES之十一——绘制3D图形
OpenGL ES之十二——地球仪和VR图

本篇概述

上一篇中介绍了使用OpenGL ES的使用流程,完成了纯色背景的绘制。但是其中没有涉及具体几何形状所以并没有使用到顶点着色器和片段着色器这篇文章中将着重介绍

绘制步骤如下:

1.配置OpenGL ES环境:在AndroidManifest.xml文件中设置使用的OpenGL ES的版本:

<!-- Tell the system this app requires OpenGL ES 3.0. -->
    <uses-feature android:glEsVersion="0x00030000" android:required="true" />

2.GLSurfaceView作为展示View,图形的具体渲染工作都是在Render中完成。
3.实现GLSurfaceView的Render,在Render中完成点,线,三角形的绘制,具体行为有:

  • 书写顶点着色器和片段着色器文件;
  • 加载编译顶点着色器和片段着色器;
  • 确定需要绘制图形的坐标和颜色数据;
  • 创建program对象,连接顶点和片元着色器,链接program对象,将坐标数据颜色数据传入OpenGL ES程序中;
  • 设置视图窗口(viewport);
  • 使颜色缓冲区的内容显示到屏幕上。

所以本文重点是:了解顶点着色器和片段着色器

一 配置OpenGL ES环境

AndroidManifest.xml文件中设置使用的OpenGL ES的版本

<!-- Tell the system this app requires OpenGL ES 3.0. -->
    <uses-feature android:glEsVersion="0x00030000" android:required="true" />

二 GLSurfaceView作为展示View

布局文件
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.opengl.GLSurfaceView
        android:id="@+id/simple_shape_gls"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
Activity
public class SimpleShapeActivity extends AppCompatActivity {
    private GLSurfaceView glSurfaceView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_simple_shape);

        initView();
    }

    private void initView() {
        glSurfaceView = findViewById(R.id.simple_shape_gls);
        glSurfaceView.setEGLContextClientVersion(3);
        GLSurfaceView.Renderer renderer = new SimpleShapeRender();
        glSurfaceView.setRenderer(renderer);
    }
}
由于下面我们要绘制三个点,由三个点连成线或组成三角形所以这里要使用顶点着色器和片段着色器这里先不详细的介绍GLSL语言,只是介绍一下AndroidStudio中配置GLSL环境和简单的介绍涉及到的一些api,后面会单独介绍一些重要的特性和api

三 AndroidStudio中配置GLSL环境

想要在Android Studio中开发GLSL需要安装相应的插件,因为默认是不支持的,所以Android Studio中不支持关键字高亮和智能提示。下面看一下如何安装插件:

3.1如下图找到Settings点击进入设置界面

在这里插入图片描述

3.2下载GLSL插件

如下图,进入Settings页面后找到Plugins选项,在上方输入框中搜索GLSL,从而找到相应插件。如果从未安装过,那么右侧的Update按钮处会是一个Install按钮。我们点击Install安装,最后点击ok完成即可。
在这里插入图片描述

3.3生成一个GLSL文件

我们在开发时候一般将GLSL文件放在raw文件夹或者Assets文件夹中,这里放在raw中。在raw中新建一个GLSl文件如下,和生成一个其他文件一样,名称为GLSL Shader。这里分为顶点着色器,片段着色器,内容按照GLSL规则编写。
在这里插入图片描述

四.顶点着色器,片段着色器程序,Render的实现

4.1顶点着色器,片段着色器文件

顶点着色器文件:vertex_simple_shade
#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec4 aColor;
out vec4 vColor;
void main() {
    gl_Position  = vPosition;
    gl_PointSize = 10.0;
    vColor = aColor;
}

第一行:着色器的版本3.0,OpenGL ES 2.0版本可以不写。
第二行:输入一个名为vPosition的4分量向量,layout (location = 0)表示这个变量的位置是顶点属性0。
第三行:输入一个名为aColor的4分量向量,layout (location = 1)表示这个变量的位置是顶点属性1。
第四行:输出一个名为vColor的4分量向量,后面输入到片段着色器中。
第五行:main函数
第六行:gl_Position赋值为vPosition
第七行:gl_PointSize 绘制点的直径10
第八行:将输入数据aColor拷贝到vColor的变量中。

gl_Position,gl_PointSize为Shader内置变量,分别为顶点位置,点的直径。

片段着色器文件fragment_simple_shade
#version 300 es
precision mediump float;
in vec4 vColor;
out vec4 fragColor;
void main() {
    fragColor = vColor;
}

第一行:着色器的版本,OpenGL ES 2.0版本可以不写。
第二行:声明着色器中浮点变量的默认精度。
第三行: 声明一个输入名为vColor的4分向量,来自上面的顶点着色器。
第四行:着色器声明一个输出变量fragColor,这个是一个4分量的向量。
第五行:mian函数
第六行:表示将输入的颜色值数据拷贝到fragColor变量中,输出到颜色缓冲区。

4.2 确定需要绘制图形的坐标和颜色数据和为顶点位置及颜色申请本地内存

绘制图形的坐标和颜色数据
	//三个顶点的位置参数
    private float triangleCoords[] = {
            0.5f, 0.5f, 0.0f, // top
            -0.5f, -0.5f, 0.0f, // bottom left
            0.5f, -0.5f, 0.0f // bottom right
    };

    //三个顶点的颜色参数
    private float color[] = {
            1.0f, 0.0f, 0.0f, 1.0f,// top
            0.0f, 1.0f, 0.0f, 1.0f,// bottom left
            0.0f, 0.0f, 1.0f, 1.0f// bottom right
    };
为顶点位置及颜色申请本地内存
public SimpleShapeRender() {
        //顶点位置相关
        //分配本地内存空间,每个浮点型占4字节空间;将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
        vertexBuffer = ByteBuffer.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        vertexBuffer.put(triangleCoords);
        vertexBuffer.position(0);

        //顶点颜色相关
        colorBuffer = ByteBuffer.allocateDirect(color.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        colorBuffer.put(color);
        colorBuffer.position(0);
 }

4.3 加载编译顶点着色器和片段着色器

/**
     * 编译
     *
     * @param type       顶点着色器:GLES30.GL_VERTEX_SHADER
     *                   片段着色器:GLES30.GL_FRAGMENT_SHADER
     * @param shaderCode 着色器语言编写的着色器程序
     * @return
     */
    private static int compileShader(int type, String shaderCode) {
        //创建一个着色器
        final int shaderId = GLES30.glCreateShader(type);
        if (shaderId != 0) {
            GLES30.glShaderSource(shaderId, shaderCode);
            GLES30.glCompileShader(shaderId);
            //检测状态
            final int[] compileStatus = new int[1];
            GLES30.glGetShaderiv(shaderId, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
            if (compileStatus[0] == 0) {
                String logInfo = GLES30.glGetShaderInfoLog(shaderId);
                System.err.println(logInfo);
                //创建失败
                GLES30.glDeleteShader(shaderId);
                return 0;
            }
            return shaderId;
        } else {
            //创建失败
            return 0;
        }
    }

4.4 创建program对象,连接顶点和片元着色器,链接program对象

	@Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //将背景设置为白色
        GLES20.glClearColor(1.0f,1.0f,1.0f,1.0f);

        //编译顶点着色程序
        String vertexShaderStr = ResReadUtils.readResource(R.raw.vertex_simple_shade);
        int vertexShaderId = ShaderUtils.compileVertexShader(vertexShaderStr);
        //编译片段着色程序
        String fragmentShaderStr = ResReadUtils.readResource(R.raw.fragment_simple_shade);
        int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShaderStr);
        //连接程序
        mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
        //在OpenGLES环境中使用程序
        GLES30.glUseProgram(mProgram);
    }

4.5 设置视图窗口(viewport);

	@Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //设置绘制窗口
        GLES30.glViewport(0, 0, width, height);
    }

4.6完成绘制;

	@Override
    public void onDrawFrame(GL10 gl) {
        //把颜色缓冲区设置为我们预设的颜色
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);

        //准备坐标数据
        GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);
        //启用顶点位置句柄
        GLES30.glEnableVertexAttribArray(0);

        //准备颜色数据
        GLES30.glVertexAttribPointer(1, 4, GLES30.GL_FLOAT, false, 0, colorBuffer);
        //启用顶点颜色句柄
        GLES30.glEnableVertexAttribArray(1);

        //绘制三个点
        //GLES30.glDrawArrays(GLES30.GL_POINTS, 0, POSITION_COMPONENT_COUNT);

        //绘制三条线
        GLES30.glLineWidth(3);//设置线宽
        GLES30.glDrawArrays(GLES30.GL_LINE_LOOP, 0, POSITION_COMPONENT_COUNT);

        //绘制三角形
        //GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, POSITION_COMPONENT_COUNT);

        //禁止顶点数组的句柄
        GLES30.glDisableVertexAttribArray(0);
        GLES30.glDisableVertexAttribArray(1);
}

4.7下面是涉及到的完整类

渲染器SimpleShapeRender
public class SimpleShapeRender implements GLSurfaceView.Renderer {
    //一个Float占用4Byte
    private static final int BYTES_PER_FLOAT = 4;
    //三个顶点
    private static final int POSITION_COMPONENT_COUNT = 3;
    //顶点位置缓存
    private final FloatBuffer vertexBuffer;
    //顶点颜色缓存
    private final FloatBuffer colorBuffer;
    //渲染程序
    private int mProgram;

    //三个顶点的位置参数
    private float triangleCoords[] = {
            0.5f, 0.5f, 0.0f, // top
            -0.5f, -0.5f, 0.0f, // bottom left
            0.5f, -0.5f, 0.0f // bottom right
    };

    //三个顶点的颜色参数
    private float color[] = {
            1.0f, 0.0f, 0.0f, 1.0f,// top
            0.0f, 1.0f, 0.0f, 1.0f,// bottom left
            0.0f, 0.0f, 1.0f, 1.0f// bottom right
    };

    public SimpleShapeRender() {
        //顶点位置相关
        //分配本地内存空间,每个浮点型占4字节空间;将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
        vertexBuffer = ByteBuffer.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        vertexBuffer.put(triangleCoords);
        vertexBuffer.position(0);

        //顶点颜色相关
        colorBuffer = ByteBuffer.allocateDirect(color.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        colorBuffer.put(color);
        colorBuffer.position(0);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //将背景设置为白色
        GLES20.glClearColor(1.0f,1.0f,1.0f,1.0f);

        //编译顶点着色程序
        String vertexShaderStr = ResReadUtils.readResource(R.raw.vertex_simple_shade);
        int vertexShaderId = ShaderUtils.compileVertexShader(vertexShaderStr);
        //编译片段着色程序
        String fragmentShaderStr = ResReadUtils.readResource(R.raw.fragment_simple_shade);
        int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShaderStr);
        //连接程序
        mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
        //在OpenGLES环境中使用程序
        GLES30.glUseProgram(mProgram);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //设置绘制窗口
        GLES30.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        //把颜色缓冲区设置为我们预设的颜色
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);

        //准备坐标数据
        GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);
        //启用顶点位置句柄
        GLES30.glEnableVertexAttribArray(0);

        //准备颜色数据
        GLES30.glVertexAttribPointer(1, 4, GLES30.GL_FLOAT, false, 0, colorBuffer);
        //启用顶点颜色句柄
        GLES30.glEnableVertexAttribArray(1);

        //绘制三个点
        //GLES30.glDrawArrays(GLES30.GL_POINTS, 0, POSITION_COMPONENT_COUNT);

        //绘制三条线
        GLES30.glLineWidth(3);//设置线宽
        GLES30.glDrawArrays(GLES30.GL_LINE_LOOP, 0, POSITION_COMPONENT_COUNT);

        //绘制三角形
        //GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, POSITION_COMPONENT_COUNT);

        //禁止顶点数组的句柄
        GLES30.glDisableVertexAttribArray(0);
        GLES30.glDisableVertexAttribArray(1);
    }
}
编译,链接工具类ShaderUtils
public class ShaderUtils {

    private static final String TAG = "ShaderUtils";

    /**
     * 编译顶点着色器
     * @param shaderCode
     */
    public static int compileVertexShader(String shaderCode) {
        return compileShader(GLES30.GL_VERTEX_SHADER, shaderCode);
    }

    /**
     * 编译片段着色器
     * @param shaderCode
     */
    public static int compileFragmentShader(String shaderCode) {
        return compileShader(GLES30.GL_FRAGMENT_SHADER, shaderCode);
    }

    /**
     * 编译
     * @param type       顶点着色器:GLES30.GL_VERTEX_SHADER
     *                   片段着色器:GLES30.GL_FRAGMENT_SHADER
     * @param shaderCode
     */
    private static int compileShader(int type, String shaderCode) {
        //创建一个着色器
        final int shaderId = GLES30.glCreateShader(type);
        if (shaderId != 0) {
            GLES30.glShaderSource(shaderId, shaderCode);
            GLES30.glCompileShader(shaderId);
            //检测状态
            final int[] compileStatus = new int[1];
            GLES30.glGetShaderiv(shaderId, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
            if (compileStatus[0] == 0) {
                String logInfo = GLES30.glGetShaderInfoLog(shaderId);
                System.err.println(logInfo);
                //创建失败
                GLES30.glDeleteShader(shaderId);
                return 0;
            }
            return shaderId;
        } else {
            //创建失败
            return 0;
        }
    }

    /**
     * 链接小程序
     * @param vertexShaderId   顶点着色器
     * @param fragmentShaderId 片段着色器
     */
    public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
        //创建一个空的OpenGLES程序
        final int programId = GLES30.glCreateProgram();
        if (programId != 0) {
            //将顶点着色器加入到程序
            GLES30.glAttachShader(programId, vertexShaderId);
            //将片元着色器加入到程序中
            GLES30.glAttachShader(programId, fragmentShaderId);
            //链接着色器程序
            GLES30.glLinkProgram(programId);
            final int[] linkStatus = new int[1];

            GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] == 0) {
                String logInfo = GLES30.glGetProgramInfoLog(programId);
                System.err.println(logInfo);
                GLES30.glDeleteProgram(programId);
                return 0;
            }
            return programId;
        } else {
            //创建失败
            return 0;
        }
    }

    /**
     * 验证程序片段是否有效
     * @param programObjectId
     */
    public static boolean validProgram(int programObjectId) {
        GLES30.glValidateProgram(programObjectId);
        final int[] programStatus = new int[1];
        GLES30.glGetProgramiv(programObjectId, GLES30.GL_VALIDATE_STATUS, programStatus, 0);
        return programStatus[0] != 0;
    }

}
资源加载工具类
public class ResReadUtils {

    /**
     * 读取资源
     * @param resourceId
     */
    public static String readResource(int resourceId) {
        StringBuilder builder = new StringBuilder();
        try {
            InputStream inputStream = App.getInstance().getResources().openRawResource(resourceId);
            InputStreamReader streamReader = new InputStreamReader(inputStream);

            BufferedReader bufferedReader = new BufferedReader(streamReader);
            String textLine;
            while ((textLine = bufferedReader.readLine()) != null) {
                builder.append(textLine);
                builder.append("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Resources.NotFoundException e) {
            e.printStackTrace();
        }
        return builder.toString();
    }

}
着重说明一个地方

在onDrawFrame中的glVertexAttribPointer和glEnableVertexAttribArray中参数比较不好理解,下面着重说一下。

1.具体使用场景
		 //准备坐标数据
        GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);
        //启用顶点位置句柄
        GLES30.glEnableVertexAttribArray(0);

        //准备颜色数据
        GLES30.glVertexAttribPointer(1, 4, GLES30.GL_FLOAT, false, 0, colorBuffer);
        //启用顶点颜色句柄
        GLES30.glEnableVertexAttribArray(1);
2.glVertexAttribPointer
public static void glVertexAttribPointer(
        int indx,
        int size,
        int type,
        boolean normalized,
        int stride,
        java.nio.Buffer ptr
    ) 
  • index:顶点属性的索引.(这里我们的顶点位置和颜色向量在着色器中分别为0和1)layout (location = 0) in vec4 vPosition; layout (location = 1) in vec4 aColor;
  • size: 指定每个通用顶点属性的元素个数。必须是1、2、3、4。此外,glvertexattribpointer接受符号常量gl_bgra。初始值为4(也就是涉及颜色的时候必为4)。
  • type:属性的元素类型。(上面都是Float所以使用GLES30.GL_FLOAT);
  • normalized:转换的时候是否要经过规范化,true:是;false:直接转化;
  • stride:跨距,默认是0。(由于我们将顶点位置和颜色数据分别存放没写在一个数组中,所以使用默认值0)
  • ptr: 本地数据缓存(这里我们的是顶点的位置和颜色数据)。
    一层层的去查看我们发现最终实现都是c方法,这也和我们第一篇中说的OpenGL的核心代码都是写的。

4.8 绘制的点,线,三角形截图

在这里插入图片描述

线

在这里插入图片描述

三角

在这里插入图片描述

4.9注意

我们会发现按照顶点数据如下

//三个顶点的位置参数
    private float triangleCoords[] = {
            0.5f, 0.5f, 0.0f, // top
            -0.5f, -0.5f, 0.0f, // bottom left
            0.5f, -0.5f, 0.0f // bottom right
    };

理应绘制成的是等腰直角三角形啊,但是现在怎么不是。这个和第一篇OpenGL ES文中提到的“使用的是虚拟坐标”有关。
同时我们的手机如果横过来的话,以三角形为例会变成如下的样子。
在这里插入图片描述
我们发现形状和竖屏的时候完全不同了(如果是三个点,或者是绘制的三条线也会出项相应的变化,因为三个顶点的位置在不同屏幕状态下“发生了变化”)。要想解决上面的问题就要了解坐标系,投影等知识放在下一篇中详细介绍。

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

智能推荐

《第一行代码》(第二版)广播的问题及其解决_代码里的广播错误-程序员宅基地

文章浏览阅读2.6k次,点赞5次,收藏13次。1)5.2.1弹出两次已连接或者未连接这是因为你同时打开了流量和WiFi,他就会发出两次广播。2)5.3.1中发送自定义广播问题标准广播未能弹出消息:Intent intent=new Intent("com.example.broadcasttest.MY_BROADCAST");sendBroadcast(intent);上述已经失效了。修改:Intent intent=new Intent("com.example.broadcasttest...._代码里的广播错误

K8s 学习者绝对不能错过的最全知识图谱(内含 58个知识点链接)-程序员宅基地

文章浏览阅读249次。作者 |平名 阿里服务端开发技术专家导读:Kubernetes 作为云原生时代的“操作系统”,熟悉和使用它是每名用户的必备技能。本篇文章概述了容器服务 Kubernet..._k8知识库

TencentOS3.1安装PHP+Nginx+redis测试系统_tencentos-3.1-程序员宅基地

文章浏览阅读923次。分别是etc/pear.conf,etc/php-fpm.conf, etc/php-fpm.d/www.conf,lib/php.ini。php8安装基本一致,因为一个服务期内有2个版本,所以注意修改不同的安装目录和端口号。可以直接使用sbin下的nginx命令启动服务。完成编译安装需要gcc支持,如果没有,使用如下命令安装。安装过程基本一致,下面是安装7.1.33的步骤。执行如下命令,检查已经安装的包和可安装的包。执行如下命令,检查已经安装的包和可安装的包。执行如下命令,检查已经安装的包和可安装的包。_tencentos-3.1

urllib.request.urlopen()基本使用_urllib.request.urlopen(url)-程序员宅基地

文章浏览阅读3.1w次,点赞21次,收藏75次。import urllib.requesturl = 'https://www.python.org'# 方式一response = urllib.request.urlopen(url)print(type(response)) # <class 'http.client.HTTPResponse'># 方式二request = urllib.request.Req..._urllib.request.urlopen(url)

如何用ChatGPT+GEE+ENVI+Python进行高光谱,多光谱成像遥感数据处理?-程序员宅基地

文章浏览阅读1.5k次,点赞12次,收藏15次。如何用ChatGPT+GEE+ENVI+Python进行高光谱,多光谱成像遥感数据处理?

RS485总线常识_rs485 差分走綫間距-程序员宅基地

文章浏览阅读1.2k次。RS485总线常识 2010-10-12 15:56:36| 分类: 知识储备 | 标签:rs485 总线 传输 差分 |字号大中小 订阅RS485总线RS485采用平衡发送和差分接收方式实现通信:发送端将串行口的TTL电平信号转换成差分信号A,B两路输出,经过线缆传输之后在接收端将差分信号还原成TTL电平信号。由于传输线通常使用双绞线,又是差分传输,所_rs485 差分走綫間距

随便推点

移植、制作uboot、Linux(一)_uboot制作-程序员宅基地

文章浏览阅读621次。u-boot、linux烧录_uboot制作

windows下安装git和gitbash安装教程_64-bit git for windows setup.-程序员宅基地

文章浏览阅读1.2w次,点赞10次,收藏44次。windos上git安装,git bash安装_64-bit git for windows setup.

环形链表(算法java)_java 实现环形链表-程序员宅基地

文章浏览阅读196次。环形链表(算法java)的两种解决方法_java 实现环形链表

docker部署Airflow(修改URL-path、更换postgres -->myslq数据库、LDAP登录)_airflow docker-程序员宅基地

文章浏览阅读5.7k次。Airflow什么是 Airflow?Airflow 的架构Airflow 解决哪些问题一、docker-compose 安装airflow(postgres)1、创建启动文件airflow-docker-compose.yml.1.1、添加挂载卷,需要修改airflow-docker-compose.yml的位置2、创建本地配置文件airflow.cfg2.1、如果想修改WEB URL地址,需要修改airflow.cfg中以下两个地方3、之后up -d直接启动即可web访问地址:二、存储数据库更换post_airflow docker

计算机毕业设计springboot高校教务管理系统532k79【附源码+数据库+部署+LW】-程序员宅基地

文章浏览阅读28次。选题背景:随着社会的发展和教育的普及,高校教务管理系统在现代高等教育中扮演着至关重要的角色。传统的手工管理方式已经无法满足高校日益增长的规模和复杂的管理需求。因此,开发一套高效、智能的教务管理系统成为了当今高校管理的迫切需求。选题意义:高校教务管理系统的开发具有重要的意义和价值。首先,它可以提高高校教务管理的效率和准确性。通过自动化处理学生选课、排课、考试安排等繁琐的事务,大大减轻了教务人员的工作负担,提高了工作效率。同时,系统可以实时更新学生信息和课程信息,减少了数据错误和冗余,保证了管理的准确性

javaint接收float_Java Integer转换double,float,int,long,string-程序员宅基地

文章浏览阅读132次。首页>基础教程>常用类>常用 Integer类Java Integer转换double,float,int,long,stringjava中Integer类可以很方便的转换成double,float,int,long,string等类型,都有固定的方法进行转换。方法double doubleValue() //以 double 类型返回该 Integer 的值。flo..._java integet接收float类型的参数

推荐文章

热门文章

相关标签