【我的OpenGL学习进阶之旅】如何抽取着色器代码到assets目录下的GLSL文件,以及如何通过Java或者C++代码来加载着GLSL文件?

article/2025/11/9 5:37:38

目录

  • 一、着色器代码以字符串形式写在代码里的现状
  • 二、抽取着色器代码为单独的GLSL文件
    • 2.1 Java中的字符串抽取为GLSL文件并加载
      • 2.1.1 Java中的字符串抽取为GLSL文件
      • 2.1.2 Java加载GLSL文件
    • 2.2 C++中的字符串抽取为GLSL文件并加载
      • 2.2.1 C++中的字符串为抽取GLSL文件
      • 2.2.2 C++加载GLSL文件
  • 三、总结

在这里插入图片描述
关于着色器的相关知识,可以参考我之前的博客介绍:

  • 【我的OpenGL学习进阶之旅】着色器和程序(上)------着色器
  • 【我的OpenGL学习进阶之旅】着色器编译器和程序二进制码
  • 【我的OpenGL学习进阶之旅】OpenGL ES 着色语言 (上)
  • 【我的OpenGL学习进阶之旅】OpenGL ES 着色语言 (下)
  • 【我的OpenGL学习进阶之旅】OpenGL ES 着色语言的IDE插件(Android Studio和Visutal Studio)以及常见GLSL文件扩展名介绍
  • 【我的OpenGL学习进阶之旅】解决着色器编译错误:#version directive must occur on the first line of the shader

一、着色器代码以字符串形式写在代码里的现状

网上大部分的例子,都是把着色器代码直接定义成字符串的形式,写出来。如下所示:

  • C++ 代码
    在这里插入图片描述
	// 顶点着色器const char* VERTEX_SHADER_TRIANGLE ="#version 300 es                          \n""layout(location = 0) in vec4 vPosition;  \n""void main()                              \n""{                                        \n""   gl_Position = vPosition;              \n""}                                        \n";// 片段着色器const char* FRAGMENT_SHADER_TRIANGLE ="#version 300 es                              \n""precision mediump float;                     \n""out vec4 fragColor;                          \n""void main()                                  \n""{                                            \n""   fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );  \n""}                                            \n";
  • Java代码
// 顶点着色器
String vShaderStr ="#version 300 es 			  \n"+ "in vec4 vPosition;           \n"+ "void main()                  \n"+ "{                            \n"+ "   gl_Position = vPosition;  \n"+ "}                            \n";// 片段着色器
String fShaderStr ="#version 300 es		 			          	\n"+ "precision mediump float;					  	\n"+ "out vec4 fragColor;	 			 		  	\n"+ "void main()                                    \n"+ "{                                              \n"+ "  fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );	    \n"+ "}                                              \n";

在这里插入图片描述
在这里插入图片描述
好吧,不知道为啥,我看到类似的代码很厌恶,完全不支持语法关键字高亮,看着贼难受!

二、抽取着色器代码为单独的GLSL文件

为此,我把着色器代码都单独抽取为GLSL文件,并使用下面博客介绍的插件
【我的OpenGL学习进阶之旅】OpenGL ES 着色语言的IDE插件(Android Studio和Visutal Studio)以及常见GLSL文件扩展名介绍
实现了语法高亮的效果。

2.1 Java中的字符串抽取为GLSL文件并加载

2.1.1 Java中的字符串抽取为GLSL文件

Java抽取GLSL文件,如下所示:

  • 顶点着色器
#version 300 es
// 表示OpenGL ES着色器语言V3.00// 使用in关键字,在顶点着色器中声明所有的输入顶点属性(Input Vertex Attribute)。
// 声明一个输入属性数组:一个名为vPosition的4分量向量
// 在图形编程中我们经常会使用向量这个数学概念,因为它简明地表达了任意空间中的位置和方向,并且它有非常有用的数学属性。
// 在GLSL中一个向量有最多4个分量,每个分量值都代表空间中的一个坐标,它们可以通过vec.x、vec.y、vec.z和vec.w来获取。
//注意vec.w分量不是用作表达空间中的位置的(我们处理的是3D不是4D),而是用在所谓透视除法(Perspective Division)上。
in vec4 vPosition;
void main()
{// 为了设置顶点着色器的输出,我们必须把位置数据赋值给预定义的gl_Position变量,它在幕后是vec4类型的。// 将vPosition输入属性拷贝到名为gl_Position的特殊输出变量// 每个顶点着色器必须在gl_Position变量中输出一个位置,这个位置传递到管线下一个阶段的位置gl_Position = vPosition;
}
  • 片段着色器
#version 300 es
// 表示OpenGL ES着色器语言V3.00// 声明着色器中浮点变量的默认精度
precision mediump float;
// 声明一个输出变量fragColor,这是一个4分量的向量,
// 写入这个变量的值将被输出到颜色缓冲器
out vec4 fragColor;
void main()
{// 所有片段的着色器输出都是红色( 1.0, 0.0, 0.0, 1.0 )fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );// 会输出橘黄色// fragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

在这里插入图片描述
可以看到语法是高亮的,看起来就舒服。

2.1.2 Java加载GLSL文件

既然抽取出去了,那就得加载它。

我们调用如下代码,指定GLSL的相对路径即可,如下所示:
在这里插入图片描述

 // 得到的结果就是一个程序对象,我们可以调用glUseProgram函数,用刚创建的程序对象作为它的参数,以激活这个程序对象mProgramObject = ESShader.loadProgramFromAsset(mContext,"shaders/vertexShader.vert","shaders/fragmentShader.frag");

ESShader内部有个readShader方法来读取assets目录下的文件,如下所示:
在这里插入图片描述

   /*** brief Read a shader source into a String* @param context    context* @param fileName  fileName Name of shader file* @return  A String object containing shader source, otherwise null*/private static String readShader(Context context, String fileName) {StringBuilder sb = new StringBuilder();try {InputStream is = context.getAssets().open(fileName);BufferedReader br = new BufferedReader(new InputStreamReader(is));String line;while ((line = br.readLine()) != null) {sb.append(line).append("\n");}} catch (IOException e) {e.printStackTrace();}return sb.toString();}

ESShader.java的完整代码如下:

// ESShader
//
//    Utility functions for loading GLSL ES 3.0 shaders and creating program objects.
//package com.openglesbook.common;import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;import android.content.Context;
import android.opengl.GLES30;
import android.util.Log;public class ESShader {/*** brief Read a shader source into a String* @param context    context* @param fileName  fileName Name of shader file* @return  A String object containing shader source, otherwise null*/private static String readShader(Context context, String fileName) {StringBuilder sb = new StringBuilder();try {InputStream is = context.getAssets().open(fileName);BufferedReader br = new BufferedReader(new InputStreamReader(is));String line;while ((line = br.readLine()) != null) {sb.append(line).append("\n");}} catch (IOException e) {e.printStackTrace();}return sb.toString();}/*** brief Load a shader, check for compile errors, print error messages to output log* @param type   Type of shader (GL_VERTEX_SHADER or GL_FRAGMENT_SHADER)* @param shaderSrc shaderSrc Shader source string* @return  A new shader object on success, 0 on failure*/public static int loadShader(int type, String shaderSrc) {int shader;int[] compiled = new int[1];// Create the shader object// 调用glCreateShader将根据传入的type参数插件一个新的顶点着色器或者片段着色器shader = GLES30.glCreateShader(type);if (shader == 0) {return 0;}// Load the shader source// glShaderSource函数把要编译的着色器对象作为第一个参数。第二参数 着色器真正的源码GLES30.glShaderSource(shader, shaderSrc);// Compile the shader// 编译着色器GLES30.glCompileShader(shader);// Check the compile status// 检测编译时的状态,是编译错误还是编译成功// pname: 获得信息的参数,可以为//      GL_COMPILE_STATUS//      GL_DELETE_STATUS//      GL_INFO_LOG_LENGTH//      GL_SHADER_SOURCE_LENGTH//      GL_SHADER_TYPEGLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);// 如果着色器编译成功,结果将是GL_TRUE。如果编译失败,结果将为GL_FALSE,编译错误将写入信息日志if (compiled[0] == 0) {// 用glGetShaderInfoLog检索信息日志Log.e("ESShader", GLES30.glGetShaderInfoLog(shader));// 删除着色器对象GLES30.glDeleteShader(shader);return 0;}return shader;}/*** brief Load a vertex and fragment shader, create a program object, link program. Errors output to log.* @param vertShaderSrc  Vertex shader source code* @param fragShaderSrc  Fragment shader source code* @return   A new program object linked with the vertex/fragment shader pair, 0 on failure*/public static int loadProgram(String vertShaderSrc, String fragShaderSrc) {int vertexShader;int fragmentShader;int programObject;int[] linked = new int[1];// Load the vertex/fragment shadersvertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertShaderSrc);if (vertexShader == 0) {return 0;}fragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragShaderSrc);if (fragmentShader == 0) {GLES30.glDeleteShader(vertexShader);return 0;}// Create the program objectprogramObject = GLES30.glCreateProgram();if (programObject == 0) {return 0;}GLES30.glAttachShader(programObject, vertexShader);GLES30.glAttachShader(programObject, fragmentShader);// Link the programGLES30.glLinkProgram(programObject);// Check the link statusGLES30.glGetProgramiv(programObject, GLES30.GL_LINK_STATUS, linked, 0);if (linked[0] == 0) {Log.e("ESShader", "Error linking program:");Log.e("ESShader", GLES30.glGetProgramInfoLog(programObject));GLES30.glDeleteProgram(programObject);return 0;}// Free up no longer needed shader resourcesGLES30.glDeleteShader(vertexShader);GLES30.glDeleteShader(fragmentShader);return programObject;}/*** brief Load a vertex and fragment shader from "assets", create a program object, link program.  Errors output to log.* @param context context* @param vertexShaderFileName  Vertex shader source file name* @param fragShaderFileName    Fragment shader source file name* @return A new program object linked with the vertex/fragment shader pair, 0 on failure*/public static int loadProgramFromAsset(Context context, String vertexShaderFileName, String fragShaderFileName) {int vertexShader;int fragmentShader;int programObject;int[] linked = new int[1];String vertShaderSrc = null;String fragShaderSrc = null;// Read vertex shader from assetsvertShaderSrc = readShader(context, vertexShaderFileName);System.out.println("  vertShaderSrc = " + vertShaderSrc);if (vertShaderSrc == null) {return 0;}// Read fragment shader from assetsfragShaderSrc = readShader(context, fragShaderFileName);System.out.println("  fragShaderSrc = " + fragShaderSrc);if (fragShaderSrc == null) {return 0;}// Load the vertex shadervertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertShaderSrc);if (vertexShader == 0) {GLES30.glDeleteShader(vertexShader);return 0;}// Load the fragment shaderfragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragShaderSrc);if (fragmentShader == 0) {GLES30.glDeleteShader(fragmentShader);return 0;}// Create the program objectprogramObject = GLES30.glCreateProgram();if (programObject == 0) {return 0;}// 在OpenGL ES3.0中,每个程序对象必须连接一个顶点着色器和一个片段着色器// 把之前编译的着色器附加到程序对象上// 着色器可以在任何时候连接-----在连接到程序之前不一定需要编译,甚至可以没有源代码。// 唯一要求是:每个程序对象必须有且只有一个顶点着色器和一个片段着色器与之连接// 除了连接着色器之外,你还可以用glDetachShader断开着色器的连接GLES30.glAttachShader(programObject, vertexShader);GLES30.glAttachShader(programObject, fragmentShader);// Link the program// 链接操作负责生成最终的可执行的程序。// 一般来说,链接阶段是生成在硬件上运行的最终硬件指令的时候GLES30.glLinkProgram(programObject);// Check the link status  检测链接着色器程序是否失败// pname 获取信息的参数,可以是//      GL_ACTIVE_ATTRIBUTES        返回顶点着色器中活动属性的数量//      GL_ACTIVE_ATTRIBUTE_MAX_LENGTH      返回最大属性名称的最大长度(以字符数表示),这一信息用于确定存储属性名字符串所需的内存量//      GL_ACTIVE_UNIFORM_BLOCK     返回包含活动统一变量的程序中的统一变量块数量//      GL_ACTIVE_UNIFORM_BLOCK_MAX_LENGTH        返回包含活动统一变量的程序中的统一变量块名称的最大长度//      GL_ACTIVE_UNIFORMS      返回活动统一变量的数量//      GL_ACTIVE_UNIFORM_MAX_LENGTH     返回最大统一变量名称的最大长度//      GL_ATTACHED_SHADERS   返回连接到程序对象的着色器数量//      GL_DELETE_STATUS   查询返回程序对象是否已经标记为删除//      GL_LINK_STATUS      检查链接是否成功//      GL_INFO_LOG_LENGTH   程序对象存储的信息日志长度//      GL_LINK_STATUS          链接是否成功//      GL_PROGRAM_BINARY_RETRIEVABLE_HINT  返回一个表示程序目前是否启用二进制检索提示的值//      GL_TRANSFORM_FEEDBACK_BUFFER_MODE   返回GL_SEPARATE_ATTRIBS 或 GL_INTERLEAVED_ATTRIBS 表示变化反馈启用时的缓冲区模式//      GL_TRANSFORM_FEEDBACK_VARYINGS     返回程序的变化反馈模式中捕捉的输出变量//      GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH        返回程序的变化反馈模式中捕捉的输出变量名称的最大长度//      GL_VALIDATE_STATUS  查询最后一个校验操作的状态GLES30.glGetProgramiv(programObject, GLES30.GL_LINK_STATUS, linked, 0);if (linked[0] == 0) {Log.e("ESShader", "Error linking program:");// 获取着色器对象的信息日志Log.e("ESShader", GLES30.glGetProgramInfoLog(programObject));// 删除一个程序对象GLES30.glDeleteProgram(programObject);return 0;}// Free up no longer needed shader resourcesGLES30.glDeleteShader(vertexShader);GLES30.glDeleteShader(fragmentShader);return programObject;}
}

2.2 C++中的字符串抽取为GLSL文件并加载

2.2.1 C++中的字符串为抽取GLSL文件

抽取出去的GLSL文件和Java抽取的类似,如下所示:

  • 顶点着色器
#version 300 es
// 表示OpenGL ES着色器语言V3.00// 使用in关键字,在顶点着色器中声明所有的输入顶点属性(Input Vertex Attribute)。
// 声明一个输入属性数组:一个名为vPosition的4分量向量
// 在图形编程中我们经常会使用向量这个数学概念,因为它简明地表达了任意空间中的位置和方向,并且它有非常有用的数学属性。
// 在GLSL中一个向量有最多4个分量,每个分量值都代表空间中的一个坐标,它们可以通过vec.x、vec.y、vec.z和vec.w来获取。
//注意vec.w分量不是用作表达空间中的位置的(我们处理的是3D不是4D),而是用在所谓透视除法(Perspective Division)上。
layout(location = 0) in vec4 vPosition;
void main()
{// 为了设置顶点着色器的输出,我们必须把位置数据赋值给预定义的gl_Position变量,它在幕后是vec4类型的。// 将vPosition输入属性拷贝到名为gl_Position的特殊输出变量// 每个顶点着色器必须在gl_Position变量中输出一个位置,这个位置传递到管线下一个阶段的位置gl_Position = vPosition;
}
  • 片段着色器
#version 300 es
// 表示OpenGL ES着色器语言V3.00// 声明着色器中浮点变量的默认精度
precision mediump float;
// 声明一个输出变量fragColor,这是一个4分量的向量,
// 写入这个变量的值将被输出到颜色缓冲器
out vec4 fragColor;void main()
{//在计算机图形中颜色被表示为有4个元素的数组:红色、绿色、蓝色和alpha(透明度)分量,//通常缩写为RGBA。当在OpenGL或GLSL中定义一个颜色的时候,//我们把颜色每个分量的强度设置在0.0到1.0之间。//比如说我们设置红为1.0f,绿为1.0f,我们会得到两个颜色的混合色,即黄色。//这三种颜色分量的不同调配可以生成超过1600万种不同的颜色!// 所有片段的着色器输出都是红色( 1.0, 0.0, 0.0, 1.0 )fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );// 会输出橘黄色// fragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

在这里插入图片描述
可以看到语法是高亮的,看起来就舒服。

2.2.2 C++加载GLSL文件

既然抽取出去了,那就得加载它。

我们调用如下代码,指定GLSL的相对路径即可,如下所示:
在这里插入图片描述

void NativeTriangle::create() {GLUtils::printGLInfo();// Main ProgramVERTEX_SHADER = GLUtils::openTextFile("vertex/vertex_shader_hello_triangle.glsl");FRAGMENT_SHADER = GLUtils::openTextFile("fragment/fragment_shader_hello_triangle.glsl");mProgram = GLUtils::createProgram(&VERTEX_SHADER, &FRAGMENT_SHADER);if (!mProgram) {LOGD("Could not create program")return;}// 设置清除颜色glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
}

GLUtils::openTextFile 代码如下所示:
在这里插入图片描述

char *GLUtils::openTextFile(const char *path) {char *buffer;FUN_BEGIN_TIME("GLUtils::openTextFile")AAsset *asset = loadAsset(path);if (asset == nullptr) {LOGE("Couldn't load %s", path)return nullptr;}off_t length = AAsset_getLength(asset);buffer = new char[length + 1];int num = AAsset_read(asset, buffer, length);AAsset_close(asset);if (num != length) {LOGE("Couldn't read %s", path)delete[] buffer;return nullptr;}buffer[length] = '\0';FUN_END_TIME("GLUtils::openTextFile")return buffer;
}

有用到 loadAsset方法,代码如下:
在这里插入图片描述

#include <android/asset_manager_jni.h>
typedef AAsset esFile;static JNIEnv *sEnv = nullptr;
static jobject sAssetManager = nullptr;void GLUtils::setEnvAndAssetManager(JNIEnv *env, jobject assetManager) {sEnv = env;sAssetManager = assetManager;
}static AAsset *loadAsset(const char *path) {AAssetManager *nativeManager = AAssetManager_fromJava(sEnv, sAssetManager);if (nativeManager == nullptr) {return nullptr;}return AAssetManager_open(nativeManager, path, AASSET_MODE_UNKNOWN);
}

代码引入了 <android/asset_manager_jni.h>,然后通过AAssetManager来打开asset文件

对了,得注意调用setEnvAndAssetManager方法,不然会引起空指针异常

在这里插入图片描述

void MyGLRenderContext::OnSurfaceCreated(JNIEnv *env, jobject assetManager) {LOGD("MyGLRenderContext::OnSurfaceCreated")// 初始化设置assetManager  一定要记得初始化,否则会报空指针异常GLUtils::setEnvAndAssetManager(env, assetManager);// ... 其他操作
}

三、总结

通过上面的介绍,我们可以把着色器代码从Java或C++的字符串中抽取出去到assets目录下的GLSL文件,然后可以很方便加载抽取出去的GLSL文件。

对于我来说,我觉得有以下几个好处:

  1. 编写GLSL代码,配合对于的GLSL插件,可以语法高亮,并且可以智能提示,写起来很顺手

  2. 很多GLSL代码是可以复用的,抽取出去之后,可以在不同的特效实现中复用它。比如加载常规Texture的着色器代码就是通用的,完全可以复用。

  3. 当然抽取出去之后需要通过文件读取的方式去加载它,可能有一定的io操作,性能会有所损坏。但是一般只会加载一次,也就还好!如下图所,有时候加载两个着色器耗时为0ms。
    在这里插入图片描述
    当然有时候也会多一点,第一次启动程序的时候用了6ms
    在这里插入图片描述

  4. 以上示例代码,可以在下面的github上找到

    • java版本代码: https://github.com/ouyangpeng/opengles3-book
    • Native版本代码:https://github.com/ouyangpeng/OpenGLESDemo

http://chatgpt.dhexx.cn/article/mvlsm4u2.shtml

相关文章

11.GLSL ES

1.数据类型&#xff1a;GLSL ES支持两种数据值类型&#xff1a;数值类型&#xff08;整型、浮点型&#xff09;和布尔值类型。GLSL ES不支持字符串类型 2.矩阵和矢量&#xff1a; GLSL ES中&#xff0c;矢量和矩阵的初始化方法遵从一般规律&#xff0c;特别的&#xff1a;初始…

opengl GLSL

GLSL 着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的&#xff0c;它包含一些针对向量和矩阵操作的有用特性。 着色器的开头总是要声明版本&#xff0c;接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数&#xff0c;在这个函数…

glsl效果2——灰度图

glsl效果2——灰度图 原始图及代码 1.1. 原始图1.2. 原始代码 灰度图效果图及glsl代码 2.1. 效果图2.2. glsl代码 加权的灰度图的效果图和glsl代码 3.1. 效果图3.2. glsl代码 git地址 以下的效果建立在使用帧缓冲&#xff0c;渲染到纹理后进行的相关操作&#xff0c;具体代…

Cesium glsl 学习

const CircleRippleMaterialSource uniform vec4 color;uniform float speed;uniform float count;uniform float gradient;czm_material czm_getMaterial(czm_materialInput materialInput){// 生成默认的基础材质czm_material material czm_getDefaultMaterial(materialInp…

GLSL 中文手册

GLSL 中文手册 基本类型: 类型说明void空类型,即不返回任何值bool布尔类型 true,falseint带符号的整数 signed integerfloat带符号的浮点数 floating scalarvec2, vec3, vec4n维浮点数向量 n-component floating point vectorbvec2, bvec3, bvec4n维布尔向量 Boolean vectori…

opengl glsl shader vscode安装插件glsl_canvas 和 shader languagesupportForVS Code

u_resolution 是画布尺寸&#xff0c;即代表画布宽高 //给内置变量gl_PointSize赋值像素大小&#xff0c;注意值是浮点数 gl_PointSize20.0; // 片元沿着x方向渐变 gl_FragColor vec4(gl_FragCoord.x/500.0*1.0,1.0,0.0,1.0); // 接收插值后的纹理坐标 varying vec2 v…

【GLSL】glsl常用函数

1.smoothstep(float edgo0,float edge1, float x); 其中的 edge0 是样条线插值的下界&#xff0c;edge1 是样条线插值的下界&#xff0c;当 x 小于下界返回 0&#xff0c;当 x 大于上界返回 1&#xff0c;介于上界下界之间生成0到1的平滑过渡 使用技巧&#xff1a; smoothstep…

高级GLSL

高级GLSL 原文Advanced GLSL作者JoeyDeVries翻译Krasjet校对暂未校对 这一小节并不会向你展示非常先进非常酷的新特性&#xff0c;也不会对场景的视觉质量有显著的提高。但是&#xff0c;这一节会或多或少涉及GLSL的一些有趣的地方以及一些很棒的技巧&#xff0c;它们可能在今…

Android音视频 - OpenGL GLSL基础

上节在绘制三角形的时候&#xff0c;简单讲解了一些着色器&#xff0c;GLSL 的相关概念&#xff0c;可能看的云里雾里的。不要担心&#xff0c;在本节中&#xff0c;我将详细讲解着色语言 GL Shader Language&#xff08;GLSL&#xff09;的一些基本的概念。 PS&#xff1a; 无…

【OpenGL学习笔记七】着色器语言(GLSL)

目录 典型着色器结构 数据类型 顶点着色器特性 片段着色器特性 从顶点着色器向片段着色器发送数据 从CPU中的应用向GPU中的着色器发送数据 GLSL规范&#xff1a;https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.1.20.pdf GLSL是类C语言 着色器是各自独立…

GLSL简介 GLSL――OpenGL Shading Language

https://blog.csdn.net/silangquan/article/details/9631341 GLSL简介 OpenGL着色语言&#xff08;GLSL――OpenGL Shading Language&#xff09;是用来在OpenGL中着色编程的语言&#xff0c;也即开发人员写的短小的自定义程序&#xff0c;他们是在图形卡的GPU &#…

uni-app 拖动滑块验证(插件分享)

插件地址&#xff1a;https://ext.dcloud.net.cn/plugin?id573 效果预览&#xff1a; 作者&#xff1a;黄河爱浪 QQ&#xff1a;1846492969&#xff0c;邮箱&#xff1a;helang.loveqq.com 公众号&#xff1a;web-7258&#xff0c;本文原创&#xff0c;著作权归作者所有&…

Android 自定义View实现拖动滑块完成验证

本文较长&#xff0c;阅读大约十分钟 此效果源自于星球的星友的提问&#xff1a; 周末忙里偷闲把效果实现了一下&#xff0c;其实这个效果一般都是在Web上的验证&#xff0c;手机App多半还是短信验证码等方式&#xff0c;或者WebView加载的JS中的效果&#xff0c;不过要自定义V…

android原生滑块验证

记录一个滑块验证&#xff0c;在文章底部会放上DEMO 简单效果图 1、滑动验证前 2、滑动验证后 用到两个类&#xff1a; 一、自定义类代码 public class ImageAuthenticationView extends android.support.v7.widget.AppCompatImageView {/*** 定义画笔*/private Paint mPai…

小程序 拖动滑块验证(自定义组件 插件分享)

效果图&#xff1a; 文章目录&#xff1a; #1 创建组件 #2 组件编码 MoveVerify.wxss MoveVerify.wxml MoveVerify.js #3 页面引用 在页面的 json 文件中引用组件 在页面的 wxml 文件中使用组件 在页面的 js 文件中接收验证回调 #1 创建组件 在小程序项目根目录创建目…

jQuery拖动滑块验证样式

样式&#xff1a; 目录结构&#xff1a; HTML代码&#xff1a; <!DOCTYPE html> <html><head><title>jQuery仿淘宝网拖动滑块验证码代码</title><meta charset"utf-8"><link href"css/drag.css" rel"stylesh…

css3 滑动验证,Vue 实现拖动滑块验证功能(只有css+js没有后台验证步骤)

vue验证滑块功能&#xff0c;在生活中很多地方都可以见到&#xff0c;那么使用起来非常方便&#xff0c;基于vue如何实现滑块验证呢&#xff1f;下面通过代码给大家讲解。 效果图如下所示&#xff1a; 拖动前 拖动后 代码引用的css与js都是线上的 将代码全部复制到一个html中可…

JavaScript实现拖动滑块验证

Write By Monkeyfly 以下内容均为原创&#xff0c;如需转载请注明出处。 前提 之前在优化别人写的登录界面时&#xff0c;遇到了滑动解锁成功后发送短信验证码的场景&#xff0c;因为涉及到改动&#xff0c;所以必须要明白它是怎么实现的。由于本人JavaScript技艺不精&#…

JavaScript实现拖动滑块验证(方法已封装)

前提 之前写了一篇博文&#xff0c;题目是《JavaScript实现拖动滑块验证》&#xff0c;里面都是用最简单的方式实现的。后来&#xff0c;群里大神推荐了一款unlock.js插件&#xff0c;称作幻灯片解锁插件。在这里附上它的github地址&#xff1a;https://github.com/menthe/unl…

JavaScript封装拖动滑块验证

原生JS封装拖动验证滑块 最终效果分析最终如何使用&#xff1f;编写库的整体初始框架编写核心函数1(创建dom和css)编写核心函数2(绑定事件)添加工具方法(核心函数2中用到的) 最终完整可运行代码使用 最终效果 分析 看到这个效果我们首先应该想到和拖动有关的api: onmousedown, …