1 前言

正方形图片贴到圆形上 中将正方形图片上的纹理映射到圆形模型上,凸镜贴图 中介绍了将圆形图片上的纹理映射到凸镜模型上。如果将原图片逐渐变为凸镜效果,中间的变化过程又是什么样的?

​ 图片的纹理中心为 tc,图片中任意一点记为 t,tc 和 t 映射到凸镜模型中的坐标分别为 vc 和 v,渐变凸镜贴图的本质是:纹理坐标不变,映射的凸镜模型的半径逐渐变小,凸镜截面扇形角逐渐变大,直到凸镜变为半球,并且在变化的过程中始终保持 v 与 vc 的距离球面距离(过 v 与 vc 的经线线段长度)不变。如下图,是凸镜贴图渐变的中间一帧和尾帧。

​ 读者如果对 OpenGL ES 不太熟悉,请回顾以下内容:

​ Unity3D Shader 版本实现见→半球卷屏特效

​ 项目目录如下:

2 案例

​ MainActivity.java

package com.zhyan8.animation.activity;

import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import com.zhyan8.animation.R;
import com.zhyan8.animation.anim.MyAnimation; public class MainActivity extends AppCompatActivity {
MyAnimation mAnimation; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} public void onClick(View view) {
mAnimation = new MyAnimation(this);
mAnimation.startAnimation();
}
}

​ activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.MainActivity">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@raw/port"
android:onClick="onClick"/>
</FrameLayout>

​ MyAnimation.java

package com.zhyan8.animation.anim;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout; import com.zhyan8.animation.opengl.MyGLSurfaceView;
import com.zhyan8.animation.opengl.MyRender; public class MyAnimation {
private Context mContext;
private ValueAnimator mValueAnimator;
private WindowManager mWindowManager;
private FrameLayout mRootView;
private MyGLSurfaceView mAnimView;
private WindowManager.LayoutParams mRootLayoutParams;
private MyRender mRender; public MyAnimation(Context context) {
mContext = context;
mValueAnimator = ValueAnimator.ofFloat(0f, 1f);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.addUpdateListener(mAnimatorUpdateListener);
mValueAnimator.addListener(mAnimatorListener);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
createView();
} public void startAnimation() {
mValueAnimator.setDuration(4000);
mValueAnimator.start();
} private void createView() {
mRootView = new FrameLayout(mContext);
mRootView.setVisibility(View.INVISIBLE);
getRootLayoutParams();
getAnimView();
mWindowManager.addView(mRootView, mRootLayoutParams);
} private void getRootLayoutParams() {
mRootLayoutParams = new WindowManager.LayoutParams();
mRootLayoutParams.x = 0;
mRootLayoutParams.y = 0;
mRootLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
mRootLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;
// 需要申请 android.permission.SYSTEM_ALERT_WINDOW 权限,并在设置中允许权限
mRootLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} private void getAnimView() {
mAnimView = new MyGLSurfaceView(mContext);
mRender = new MyRender(mContext.getResources());
mAnimView.init(mRender);
mAnimView.setVisibility(View.INVISIBLE);
mRootView.addView(mAnimView);
} private ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float fraction = mValueAnimator.getAnimatedFraction();
if (fraction < 0.5f) {
mRender.setFanAngle(fraction * 360);
} else {
float scale = 1.8f - 1.6f * fraction;
mRender.setScale(scale);
}
mAnimView.requestRender();
}
}; private Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
mRootView.setVisibility(View.VISIBLE);
mAnimView.setVisibility(View.VISIBLE);
} @Override
public void onAnimationEnd(Animator animation) {
mRootView.removeView(mAnimView);
mWindowManager.removeView(mRootView);
mAnimView = null;
mRootView = null;
} @Override
public void onAnimationCancel(Animator animation) {} @Override
public void onAnimationRepeat(Animator animation) {}
};
}

​ 注意:由于创建的 mRootView 的 type 为 TYPE_APPLICATION_OVERLAY,需要在 AndroidManifest.xml 中 标签下申请如下权限,并且需要在 “设置>应用管理>权限” 中允许权限。

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

​ MyGLSurfaceView.java

package com.zhyan8.animation.opengl;

import android.content.Context;
import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet; public class MyGLSurfaceView extends GLSurfaceView {
public MyGLSurfaceView(Context context) {
super(context);
setEGLContextClientVersion(3);
} public MyGLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
setEGLContextClientVersion(3);
} public void init(MyRender render) {
setEGLConfigChooser(8, 8, 8, 8, 16, 0);
getHolder().setFormat(PixelFormat.TRANSLUCENT);
setZOrderOnTop(true);
setRenderer(render);
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
}

​ MyRender.java

package com.zhyan8.animation.opengl;

import android.content.res.Resources;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView; import com.zhyan8.animation.model.Model; import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10; public class MyRender implements GLSurfaceView.Renderer {
private Model mModel; public MyRender(Resources resources) {
mModel = new Model(resources);
} @Override
public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) {
//设置背景颜色
GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
//启动深度测试
gl.glEnable(GLES30.GL_DEPTH_TEST);
//创建程序id
mModel.onModelCreate();
} @Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置视图窗口
GLES30.glViewport(0, 0, width, height);
mModel.onModelChange(width, height);
} @Override
public void onDrawFrame(GL10 gl) {
//将颜色缓冲区设置为预设的颜色
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);
//启用顶点的数组句柄
GLES30.glEnableVertexAttribArray(0);
GLES30.glEnableVertexAttribArray(1);
//绘制模型
mModel.onModelDraw();
//禁止顶点数组句柄
GLES30.glDisableVertexAttribArray(0);
GLES30.glDisableVertexAttribArray(1);
} public void setFanAngle(float angle) {
mModel.setFanAngle(angle);
} public void setScale(float scale) {
mModel.setScale(scale);
}
}

​ Model.java

package com.zhyan8.animation.model;

import android.content.res.Resources;
import android.graphics.Point;
import android.opengl.GLES30; import com.zhyan8.animation.R;
import com.zhyan8.animation.utils.ArraysUtils;
import com.zhyan8.animation.utils.ShaderUtils;
import com.zhyan8.animation.utils.TextureUtils; import java.nio.FloatBuffer; public class Model {
private static final int ROW_NUM = 60; // 纹理行数
private static final int COL_NUM = 60; // 纹理列数
private static final float ROW_WIDTH = 1.0f / ROW_NUM; // 每行宽度
private static final float COL_WIDTH = 1.0f / COL_NUM; // 每列宽度
private static final int TEXTURE_DIMENSION = 2; // 纹理坐标维度
private static final int VERTEX_DIMENSION = 3; // 顶点坐标维度
private static final double EPSILON = 0.00000000001; // epsilon,比较小的浮点常量
private Resources mResources;
private MyTransform mTransform;
private float[][] mTextures;
private float[][] mVertices;
private FloatBuffer[] mTexturesBuffers;
private FloatBuffer[] mVerticesBuffers;
private int mTextureId;
private int mProgramId;
private int mPointNumPerRow;
private float mHalfFanAngle = (float) (Math.PI / 2); // 圆锥凸镜截面扇形弧度
private float mBitmapRatio = 1.0f; public Model(Resources resources) {
mResources = resources;
mPointNumPerRow = (COL_NUM + 1) * 2;
mTextures = new float[ROW_NUM][mPointNumPerRow * TEXTURE_DIMENSION];
mVertices = new float[ROW_NUM][mPointNumPerRow * VERTEX_DIMENSION];
mTexturesBuffers = new FloatBuffer[ROW_NUM];
mVerticesBuffers = new FloatBuffer[ROW_NUM];
mTransform = new MyTransform();
} // 模型创建
public void onModelCreate() {
mProgramId = ShaderUtils.createProgram(mResources, R.raw.vertex_shader, R.raw.fragment_shader);
Point bitmapSize = new Point();
mTextureId = TextureUtils.loadTexture(mResources, R.raw.port, bitmapSize);
mBitmapRatio = 1.0f * bitmapSize.y / bitmapSize.x;
mTransform.onTransformCreate(mProgramId);
computeTextureAndVertex();
} // 模型参数变化
public void onModelChange(int width, int height) {
mTransform.onTransformChange(width, height);
} // 模型绘制
public void onModelDraw() {
GLES30.glUseProgram(mProgramId);
mTransform.onTransformExecute();
for (int i = 0; i < ROW_NUM; i++) { // 一行一行绘制纹理
//准备顶点坐标和纹理坐标
GLES30.glVertexAttribPointer(0, VERTEX_DIMENSION, GLES30.GL_FLOAT, false, 0, mVerticesBuffers[i]);
GLES30.glVertexAttribPointer(1, TEXTURE_DIMENSION, GLES30.GL_FLOAT, false, 0, mTexturesBuffers[i]);
//激活纹理
GLES30.glActiveTexture(GLES30.GL_TEXTURE);
//绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureId);
//绘制贴图
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mPointNumPerRow);
// GLES30.glDrawArrays(GLES30.GL_LINE_STRIP, 0, mPointNumPerRow);
}
} // 设置圆锥截面扇形角
public void setFanAngle(float angle) {
mHalfFanAngle = (float) (angle / 180.0 * Math.PI / 2);
computeTextureAndVertex();
} // 设置模型缩放比
public void setScale(float scale) {
mTransform.setScale(scale);
} // 计算纹理和顶点坐标
private void computeTextureAndVertex() {
for (int i = 0; i < ROW_NUM; i++) {
getRowTexture(i);
getRowVertex(i);
mTexturesBuffers[i] = ArraysUtils.getFloatBuffer(mTextures[i]);
mVerticesBuffers[i] = ArraysUtils.getFloatBuffer(mVertices[i]);
}
} // 计算纹理坐标
private void getRowTexture(int row) {
int index = 0;
float y1 = row * ROW_WIDTH;
float y2 = y1 + ROW_WIDTH;
float x = 0;
for (int i = 0; i <= COL_NUM; i++) {
mTextures[row][index++] = x;
mTextures[row][index++] = y1;
mTextures[row][index++] = x;
mTextures[row][index++] = y2;
x += COL_WIDTH;
}
} // 计算顶点坐标
private void getRowVertex(int row) {
int index = 0;
for (int i = 0; i < mTextures[row].length; i += TEXTURE_DIMENSION) {
float[] pos = getPosInWorldAxis(mTextures[row][i], mTextures[row][i + 1]);
pos[1] *= mBitmapRatio;
textureVertexMapping(pos);
mVertices[row][index++] = pos[0];
mVertices[row][index++] = pos[1];
mVertices[row][index++] = pos[2];
}
} // 纹理坐标映射到顶点坐标
private void textureVertexMapping(float[] pos) {
double norm = Math.sqrt(pos[0] * pos[0] + pos[1] * pos[1]);
double alpha = mHalfFanAngle;
double beta = norm * alpha;
if (norm > EPSILON) {
double factor = Math.sin(beta) / Math.sin(alpha) / norm;
pos[0] = (float) (pos[0] * factor);
pos[1] = (float) (pos[1] * factor);
}
pos[2] = (float) ((Math.cos(beta) - Math.cos(alpha)) / Math.sin(alpha));
} //纹理坐标转换为世界坐标
private float[] getPosInWorldAxis(float x, float y) {
float [] pos = new float[VERTEX_DIMENSION];
pos[0] = x * 2 - 1;
pos[1] = 1 - y * 2;
pos[2] = 1f;
return pos;
}
}

​ MyTransform.java

package com.zhyan8.animation.model;

import android.opengl.GLES30;
import android.opengl.Matrix; public class MyTransform {
private int mProgramId;
private float mRatio;
private float mCurrScale = 1.0f;
private int mMvpMatrixHandle;
private float[] mModelMatrix;
private float[] mViewMatrix;
private float[] mProjectionMatrix;
private float[] mMvpMatrix; // 变换创建
public void onTransformCreate(int programId) {
mProgramId = programId;
mMvpMatrixHandle = GLES30.glGetUniformLocation(mProgramId, "mvpMatrix");
mViewMatrix = getIdentityMatrix(16, 0);
mMvpMatrix = getIdentityMatrix(16, 0);
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 6.4f, 0, 0, 0, 0, 1, 0);
} // 变换参数变换
public void onTransformChange(int width, int height) {
mRatio = 1.0f * width / height;
mProjectionMatrix = getIdentityMatrix(16, 0);
Matrix.frustumM(mProjectionMatrix, 0, -mRatio, mRatio, -1, 1, 3, 20);
} // 变换执行
public void onTransformExecute() {
mModelMatrix = getIdentityMatrix(16, 0);
Matrix.scaleM(mModelMatrix, 0, mCurrScale, mCurrScale, mCurrScale);
//计算MVP变换矩阵: mvpMatrix = projectionMatrix * viewMatrix * modelMatrix
float[] tempMatrix = new float[16];
Matrix.multiplyMM(tempMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
Matrix.multiplyMM(mMvpMatrix, 0, mProjectionMatrix, 0, tempMatrix, 0);
GLES30.glUniformMatrix4fv(mMvpMatrixHandle, 1, false, mMvpMatrix, 0);
} // 设置模型缩放比
public void setScale(float scale) {
mCurrScale = scale;
} private float[] getIdentityMatrix(int size, int offset) {
float[] matrix = new float[size];
Matrix.setIdentityM(matrix, offset);
return matrix;
}
}

​ ShaderUtils.java

package com.zhyan8.animation.utils;

import android.content.res.Resources;
import android.opengl.GLES30;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader; public class ShaderUtils {
//创建程序id
public static int createProgram(Resources resources, int vertexShaderResId, int fragmentShaderResId) {
final int vertexShaderId = compileShader(resources, GLES30.GL_VERTEX_SHADER, vertexShaderResId);
final int fragmentShaderId = compileShader(resources, GLES30.GL_FRAGMENT_SHADER, fragmentShaderResId);
return linkProgram(vertexShaderId, fragmentShaderId);
} //通过外部资源编译着色器
private static int compileShader(Resources resources, int type, int shaderId){
String shaderCode = readShaderFromResource(resources, shaderId);
return compileShader(type, shaderCode);
} //通过代码片段编译着色器
private static int compileShader(int type, String shaderCode){
int shader = GLES30.glCreateShader(type);
GLES30.glShaderSource(shader, shaderCode);
GLES30.glCompileShader(shader);
return shader;
} //链接到着色器
private static int linkProgram(int vertexShaderId, int fragmentShaderId) {
final int programId = GLES30.glCreateProgram();
//将顶点着色器加入到程序
GLES30.glAttachShader(programId, vertexShaderId);
//将片元着色器加入到程序
GLES30.glAttachShader(programId, fragmentShaderId);
//链接着色器程序
GLES30.glLinkProgram(programId);
return programId;
} //从shader文件读出字符串
private static String readShaderFromResource(Resources resources, int shaderId) {
InputStream is = resources.openRawResource(shaderId);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
StringBuilder sb = new StringBuilder();
try {
while ((line = br.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
}

​ TextureUtils.java

package com.zhyan8.animation.utils;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.opengl.GLES30;
import android.opengl.GLUtils; public class TextureUtils {
//加载纹理贴图
public static int loadTexture(Resources resources, int resourceId, Point outBitmapSize) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap bitmap = BitmapFactory.decodeResource(resources, resourceId, options);
outBitmapSize.set(bitmap.getWidth(), bitmap.getHeight());
final int[] textureIds = new int[1];
// 生成纹理id
GLES30.glGenTextures(1, textureIds, 0);
// 绑定纹理到OpenGL
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0]);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR_MIPMAP_LINEAR);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
// 加载bitmap到纹理中
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0);
// 生成MIP贴图
GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);
// 取消绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
return textureIds[0];
}
}

​ ArraysUtils.java

package com.zhyan8.animation.utils;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer; public class ArraysUtils {
public static FloatBuffer getFloatBuffer(float[] floatArr) {
FloatBuffer fb = ByteBuffer.allocateDirect(floatArr.length * Float.BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
fb.put(floatArr);
fb.position(0);
return fb;
}
}

​ vertex_shader.glsl

#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTextureCoord;
uniform mat4 mvpMatrix;
out vec2 vTexCoord;
void main() {
gl_Position = mvpMatrix * vPosition;
vTexCoord = aTextureCoord;
}

​ fragment_shader.glsl

#version 300 es
precision mediump float;
uniform sampler2D uTextureUnit;
in vec2 vTexCoord;
out vec4 fragColor;
void main() {
fragColor = texture(uTextureUnit,vTexCoord);
}

3 运行效果

​ 声明:本文转自【OpenGL ES】渐变凸镜贴图

【OpenGL ES】渐变凸镜贴图的更多相关文章

  1. OpenGL ES无法获取贴图数据原因

    最近在做一个项目,要从贴图中获取图像数据,查了很多资料,也琢磨很久,获取到的数据都是0.终于在一次偶然的机会,发现了端倪,成功了. 不得不说这"一分灵感"真的很重要 以下是在获取贴 ...

  2. 2.x最终照着教程,成功使用OpenGL ES 绘制纹理贴图,添加了灰度图

    在之前成功绘制变色的几何图形之后,今天利用Openg ES的可编程管线绘制出第一张纹理. 学校时候不知道OpenGL的重要性,怕晦涩的语法.没有跟老师学习OpenGL的环境配置,现在仅仅能利用coco ...

  3. Opengl ES之纹理贴图

    纹理可以理解为一个二维数组,它可以存储大量的数据,这些数据可以发送到着色器上.一般情况下我们所说的纹理是表示一副2D图,此时纹理存储的数据就是这个图的像素数据. 所谓的纹理贴图,就是使用Opengl将 ...

  4. Android OpenGL ES(一)OpenGL ES介绍

    在学习Android OpenGL ES开发之前,你必须具备Java 语言开发经验和一些Android开发的基本知识,但并不需要有图形开发的经验,本教程也会涉及到一些基本的线性几何知识,如矢量,矩阵运 ...

  5. (转)Android OpenGL ES(一)

    转:http://wiki.jikexueyuan.com/project/opengl-es-guide/pipeline.html OpenGL ES 主要用来开发 3D 图形应用的.OpenGL ...

  6. Opengl ES之VBO和VAO

    前言 本文主要介绍了什么是VBO/VAO,为什么需要使用VBO/VAO以及如何使用VBO和VAO. VBO 什么是VBO VBO(vertex Buffer Object):顶点缓冲对象.是在显卡存储 ...

  7. Opengl ES之FBO

    FBO介绍 FBO帧缓冲对象,它的主要作用一般就是用作离屏渲染,例如做Camera相机图像采集进行后期处理时就可能会用到FBO.假如相机出图的是OES纹理,为了方便后期处理, 一般先将OES纹理通过F ...

  8. Opengl ES之YUV数据渲染

    YUV回顾 记得在音视频基础知识介绍中,笔者专门介绍过YUV的相关知识,可以参考: <音视频基础知识-YUV图像> YUV数据量相比RGB较小,因此YUV适用于传输,但是YUV图不能直接用 ...

  9. Opengl ES之踩坑记

    前因 最近在尝试使用Opengl ES实现一些LUT滤镜效果,在实现这些滤镜效果的时候遇到一些兼容性的坑,踩过这些坑后我希望把这几个坑分享给读者朋友们, 希望同在学习Opengl ES的朋友们能少走弯 ...

  10. Opengl ES之矩阵变换(上)

    前言 说到矩阵变换,我们第一时间想到的就是大学时代的线性代数这些复杂的东西,突然有了一种令人从入门到放弃的念头,不慌,作为了一个应用层的CV工程师, 在实际应用中线性代数哪些复杂的计算根本不用我们自己 ...

随机推荐

  1. 【SHELL】命令补全

    # 指定文件 dodo_path=/home/skull/work/scripts/dodo echo "hello skull" ## COMP_WORDS 是一个 bash 内 ...

  2. Go-并发安全map

  3. [转帖]nginx 剖析 request_time和upstream_response_time的误区、区别

    https://cloud.tencent.com/developer/article/1767981 首先,澄清一个误区 upstream_response_time必须在upstream配置时才能 ...

  4. [转帖] 在Linux上查看活跃线程数与连接数

    https://www.cnblogs.com/codelogs/p/17178675.html   原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,非公众号转载保留此声明. 简介# 现 ...

  5. 【转帖】ARM 虚拟化技术简介

    一. 虚拟化技术二. 虚拟化技术的比较2.1 全虚拟化和二进制重写(Pure virtualization and binary rewriting)2.2 半虚拟化( Para-virtualiza ...

  6. [转帖]解读内核 sysctl 配置中 panic、oops 相关项目

    写在前面 本篇文章的内容主要来自内核源码树 Documentation/admin-guide/sysctl/kernel.rst文件. softlockup vs hardlockup softlo ...

  7. 申威CPU的简单知识梳理

    摘要 最近有客户要用申威服务器了 自己很早之前简单测试过相关的CPU的服务器 但是感觉很多东西都不是很系统. 今天简单收集一下资料 希望对以后的工作有所帮助 申威CPU的创始 申威是解放军总参谋部第五 ...

  8. zabbix 6.0 官方文档

    Choose your platform   ZABBIX VERSION 6.0 LTS 5.4 5.0 LTS 4.0 LTS OS DISTRIBUTION Red Hat Enterprise ...

  9. 手写promise异步状态修改then方法返回来的结果

    看看下面这一段代码返回来的是什么??? <body> <script type="text/javascript"> let p = new Promise ...

  10. vim 从嫌弃到依赖(11)——标签页操作

    各位小伙伴们,你们好,我又更新了! 之前介绍了vim关于多窗口的操作,vim中多窗口是将多个窗口在一个屏幕中显示,这似乎与我们常见的ide有点不一样,一般的ide都是新开一个窗口之后在新的标签页中显示 ...