【Filament】纹理贴图

1 前言

        本文主要介绍使用 Filament 实现纹理贴图,读者如果对 Filament 不太熟悉,请回顾以下内容。

  • Filament环境搭建
  • 绘制三角形
  • 绘制矩形
  • 绘制圆形
  • 绘制立方体

        Filament 纹理坐标的 x、y 轴正方向分别朝右和朝上,其 y 轴正方向朝向与 OpenGL ES 和 libGDX 相反(详见【OpenGL ES】纹理贴图、【libGDX】Mesh纹理贴图),如下。

2 纹理贴图

        本文项目结构如下,完整代码资源 → Filament纹理贴图。 

2.1 自定义基类

        为方便读者将注意力聚焦在 Filament 的输入上,轻松配置复杂的环境依赖逻辑,笔者仿照 OpenGL ES 的写法,抽出了 FLSurfaceView 和 BaseModel 类。FLSurfaceView 与 GLSurfaceView 的功能类似,承载了渲染环境配置;BaseModel 中提供了一些 VertexBuffer、IndexBuffer、Material、Renderable 相关的工具类,方便子类直接使用这些工具类。

        build.gradle

...
android {...aaptOptions { // 在应用程序打包过程中不压缩的文件noCompress 'filamat', 'ktx'}
}dependencies {implementation fileTree(dir: '../libs', include: ['*.aar'])...
}

        说明:在项目根目录下的 libs 目录中,需要放入以下 aar 文件,它们源自Filament环境搭建中编译生成的 aar。

        FLSurfaceView.java

package com.zhyan8.texture.filament;import android.content.Context;
import android.graphics.Point;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceView;import com.google.android.filament.Camera;
import com.google.android.filament.Engine;
import com.google.android.filament.EntityManager;
import com.google.android.filament.Filament;
import com.google.android.filament.Renderer;
import com.google.android.filament.Scene;
import com.google.android.filament.Skybox;
import com.google.android.filament.SwapChain;
import com.google.android.filament.View;
import com.google.android.filament.Viewport;
import com.google.android.filament.android.DisplayHelper;
import com.google.android.filament.android.FilamentHelper;
import com.google.android.filament.android.UiHelper;import java.util.ArrayList;/** Filament中待渲染的SurfaceView* 功能可以类比OpenGL ES中的GLSurfaceView* 用于创建Filament的渲染环境*/
public class FLSurfaceView extends SurfaceView {public static int RENDERMODE_WHEN_DIRTY = 0; // 用户请求渲染才渲染一帧public static int RENDERMODE_CONTINUOUSLY = 1; // 持续渲染protected int mRenderMode = RENDERMODE_CONTINUOUSLY; // 渲染模式protected Choreographer mChoreographer; // 消息控制protected DisplayHelper mDisplayHelper; // 管理Display(可以监听分辨率或刷新率的变化)protected UiHelper mUiHelper; // 管理SurfaceView、TextureView、SurfaceHolderprotected Engine mEngine; // 引擎(跟踪用户创建的资源, 管理渲染线程和硬件渲染器)protected Renderer mRenderer; // 渲染器(用于操作系统窗口, 生成绘制命令, 管理帧延时)protected Scene mScene; // 场景(管理渲染对象、灯光)protected View mView; // 存储渲染数据(View是Renderer操作的对象)protected Camera mCamera; // 相机(视角管理)protected Point mDesiredSize; // 渲染分辨率protected float[] mSkyboxColor; // 背景颜色protected SwapChain mSwapChain; // 操作系统的本地可渲染表面(native renderable surface, 通常是一个window或view)protected FrameCallback mFrameCallback = new FrameCallback(); // 帧回调protected ArrayList<RenderCallback> mRenderCallbacks; // 每一帧渲染前的回调(一般用于处理模型变换、相机变换等)static {Filament.init();}public FLSurfaceView(Context context) {super(context);mChoreographer = Choreographer.getInstance();mDisplayHelper = new DisplayHelper(context);mRenderCallbacks = new ArrayList<>();}public void init() { // 初始化setupSurfaceView();setupFilament();setupView();setupScene();}public void setRenderMode(int renderMode) { // 设置渲染模式mRenderMode = renderMode;}public void addRenderCallback(RenderCallback renderCallback) { // 添加渲染回调if (renderCallback != null) {mRenderCallbacks.add(renderCallback);}}public void requestRender() { // 请求渲染mChoreographer.postFrameCallback(mFrameCallback);}public void onResume() { // 恢复mChoreographer.postFrameCallback(mFrameCallback);}public void onPause() { // 暂停mChoreographer.removeFrameCallback(mFrameCallback);}public void onDestroy() { // 销毁Filament环境mChoreographer.removeFrameCallback(mFrameCallback);mRenderCallbacks.clear();mUiHelper.detach();mEngine.destroyRenderer(mRenderer);mEngine.destroyView(mView);mEngine.destroyScene(mScene);mEngine.destroyCameraComponent(mCamera.getEntity());EntityManager entityManager = EntityManager.get();entityManager.destroy(mCamera.getEntity());mEngine.destroy();}protected void setupScene() { // 设置Scene参数}protected void onResized(int width, int height) { // Surface尺寸变化时回调double zoom = 1;double aspect = (double) width / (double) height;mCamera.setProjection(Camera.Projection.ORTHO,-aspect * zoom, aspect * zoom, -zoom, zoom, 0, 1000);}private void setupSurfaceView() { // 设置SurfaceViewmUiHelper = new UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK);mUiHelper.setRenderCallback(new SurfaceCallback());if (mDesiredSize != null) {mUiHelper.setDesiredSize(mDesiredSize.x, mDesiredSize.y);}mUiHelper.attachTo(this);}private void setupFilament() { // 设置Filament参数mEngine = Engine.create();// mEngine = (new Engine.Builder()).featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build();mRenderer = mEngine.createRenderer();mScene = mEngine.createScene();mView = mEngine.createView();mCamera = mEngine.createCamera(mEngine.getEntityManager().create());}private void setupView() { // 设置View参数float[] color = mSkyboxColor != null ? mSkyboxColor : new float[] {0, 0, 0, 1};Skybox skybox = (new Skybox.Builder()).color(color).build(mEngine);mScene.setSkybox(skybox);if (mEngine.getActiveFeatureLevel() == Engine.FeatureLevel.FEATURE_LEVEL_0) {mView.setPostProcessingEnabled(false); // FEATURE_LEVEL_0不支持post-processing}mView.setCamera(mCamera);mView.setScene(mScene);}/** 帧回调*/private class FrameCallback implements Choreographer.FrameCallback {@Overridepublic void doFrame(long frameTimeNanos) { // 渲染每帧数据if (mRenderMode == RENDERMODE_CONTINUOUSLY) {mChoreographer.postFrameCallback(this); // 请求下一帧}mRenderCallbacks.forEach(callback -> callback.onCall());if (mUiHelper.isReadyToRender()) {if (mRenderer.beginFrame(mSwapChain, frameTimeNanos)) {mRenderer.render(mView);mRenderer.endFrame();}}}}/** Surface回调*/private class SurfaceCallback implements UiHelper.RendererCallback {@Overridepublic void onNativeWindowChanged(Surface surface) { // Native窗口改变时回调if (mSwapChain != null) {mEngine.destroySwapChain(mSwapChain);}long flags = mUiHelper.getSwapChainFlags();if (mEngine.getActiveFeatureLevel() == Engine.FeatureLevel.FEATURE_LEVEL_0) {if (SwapChain.isSRGBSwapChainSupported(mEngine)) {flags = flags | SwapChain.CONFIG_SRGB_COLORSPACE;}}mSwapChain = mEngine.createSwapChain(surface, flags);mDisplayHelper.attach(mRenderer, getDisplay());}@Overridepublic void onDetachedFromSurface() { // 解绑Surface时回调mDisplayHelper.detach();if (mSwapChain != null) {mEngine.destroySwapChain(mSwapChain);mEngine.flushAndWait();mSwapChain = null;}}@Overridepublic void onResized(int width, int height) { // Surface尺寸变化时回调mView.setViewport(new Viewport(0, 0, width, height));FilamentHelper.synchronizePendingFrames(mEngine);FLSurfaceView.this.onResized(width, height);}}/** 每一帧渲染前的回调* 一般用于处理模型变换、相机变换等*/public static interface RenderCallback {void onCall();}
}

        BaseModel.java

package com.zhyan8.texture.filament;import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.EntityManager;
import com.google.android.filament.IndexBuffer;
import com.google.android.filament.Material;
import com.google.android.filament.MaterialInstance;
import com.google.android.filament.RenderableManager;
import com.google.android.filament.RenderableManager.PrimitiveType;
import com.google.android.filament.Texture;
import com.google.android.filament.TransformManager;
import com.google.android.filament.VertexBuffer;
import com.google.android.filament.VertexBuffer.AttributeType;
import com.google.android.filament.VertexBuffer.VertexAttribute;
import com.google.android.filament.android.TextureHelper;import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;/** 模型基类* 管理模型的材质、顶点属性、顶点索引、渲染id*/
public class BaseModel {private static String TAG = "BaseModel";protected Context mContext; // 上下文protected Engine mEngine; // Filament引擎protected TransformManager mTransformManager; // 模型变换管理器protected Material mMaterial; // 模型材质protected MaterialInstance mMaterialInstance; // 模型材质实例protected VertexBuffer mVertexBuffer; // 顶点属性缓存protected IndexBuffer mIndexBuffer; // 顶点索引缓存protected int mRenderable; // 渲染idprotected int mTransformComponent; // 模型变换组件的idprotected Box mBox; // 渲染区域protected FLSurfaceView.RenderCallback mRenderCallback; // 每一帧渲染前的回调(一般用于处理模型变换、相机变换等)public BaseModel(Context context, Engine engine) {mContext = context;mEngine = engine;mTransformManager = mEngine.getTransformManager();}public Material getMaterial() { // 获取材质return mMaterial;}public VertexBuffer getVertexBuffer() { // 获取顶点属性缓存return mVertexBuffer;}public IndexBuffer getIndexBuffer() { // 获取顶点索引缓存return mIndexBuffer;}public int getRenderable() { // 获取渲染idreturn mRenderable;}public FLSurfaceView.RenderCallback getRenderCallback() { // 获取渲染回调return mRenderCallback;}public void destroy() { // 销毁模型mEngine.destroyEntity(mRenderable);mEngine.destroyVertexBuffer(mVertexBuffer);mEngine.destroyIndexBuffer(mIndexBuffer);mEngine.destroyMaterialInstance(mMaterialInstance);mEngine.destroyMaterial(mMaterial);EntityManager entityManager = EntityManager.get();entityManager.destroy(mRenderable);}protected Material loadMaterial(String materialPath) { // 加载材质Buffer buffer = readUncompressedAsset(materialPath);if (buffer != null) {Material material = (new Material.Builder()).payload(buffer, buffer.remaining()).build(mEngine);mMaterialInstance = material.createInstance();material.compile(Material.CompilerPriorityQueue.HIGH,Material.UserVariantFilterBit.ALL,new Handler(Looper.getMainLooper()),() -> Log.i(TAG, "Material " + material.getName() + " compiled."));mEngine.flush();return material;}return null;}protected VertexBuffer getVertexBuffer(float[] values) { // 获取顶点属性缓存ByteBuffer vertexData = getByteBuffer(values);int vertexCount = values.length / 3;int vertexSize = Float.BYTES * 3;VertexBuffer vertexBuffer = new VertexBuffer.Builder().bufferCount(1).vertexCount(vertexCount).attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize).build(mEngine);vertexBuffer.setBufferAt(mEngine, 0, vertexData);return vertexBuffer;}protected VertexBuffer getVertexBuffer(VertexPosCol[] values) { // 获取顶点属性缓存ByteBuffer vertexData = getByteBuffer(values);int vertexCount = values.length;int vertexSize = VertexPosCol.BYTES;VertexBuffer vertexBuffer = new VertexBuffer.Builder().bufferCount(1).vertexCount(vertexCount).attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize).attribute(VertexAttribute.COLOR,    0, AttributeType.UBYTE4, 3 * Float.BYTES, vertexSize).normalized(VertexAttribute.COLOR).build(mEngine);vertexBuffer.setBufferAt(mEngine, 0, vertexData);return vertexBuffer;}protected VertexBuffer getVertexBuffer(VertexPosUV[] values) { // 获取顶点属性缓存ByteBuffer vertexData = getByteBuffer(values);int vertexCount = values.length;int vertexSize = VertexPosUV.BYTES;VertexBuffer vertexBuffer = new VertexBuffer.Builder().bufferCount(1).vertexCount(vertexCount).attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize).attribute(VertexAttribute.UV0,    0, AttributeType.FLOAT2, 3 * Float.BYTES, vertexSize).build(mEngine);vertexBuffer.setBufferAt(mEngine, 0, vertexData);return vertexBuffer;}protected IndexBuffer getIndexBuffer(short[] values) { // 获取顶点索引缓存ByteBuffer indexData = getByteBuffer(values);int indexCount = values.length;IndexBuffer indexBuffer = new IndexBuffer.Builder().indexCount(indexCount).bufferType(IndexBuffer.Builder.IndexType.USHORT).build(mEngine);indexBuffer.setBuffer(mEngine, indexData);return indexBuffer;}protected int getRenderable(PrimitiveType primitiveType, int vertexCount) { // 获取渲染idint renderable = EntityManager.get().create();new RenderableManager.Builder(1).boundingBox(mBox).geometry(0, primitiveType, mVertexBuffer, mIndexBuffer, 0, vertexCount).material(0, mMaterialInstance).build(mEngine, renderable);return renderable;}protected Texture getTexture(String texturePath) { // 获取TextureBitmap bitmap = loadBitmapFromAsset(texturePath);if (bitmap != null) {return generateTexture(bitmap);}return null;}protected Texture getTexture(int resourceId) { // 获取TextureBitmap bitmap = loadBitmapFromDrawable(resourceId);if (bitmap != null) {return generateTexture(bitmap);}return null;}private Texture generateTexture(Bitmap bitmap) { // 生成TextureTexture texture = new Texture.Builder().width(bitmap.getWidth()).height(bitmap.getHeight()).sampler(Texture.Sampler.SAMPLER_2D).format(Texture.InternalFormat.SRGB8_A8).levels(0xff).build(mEngine);TextureHelper.setBitmap(mEngine, texture, 0, bitmap, new Handler(), () ->Log.i(TAG, "getTexture, Bitmap is released."));texture.generateMipmaps(mEngine);return texture;}private Buffer readUncompressedAsset(String assetPath) { // 加载资源ReadableByteChannel src = null;FileInputStream fis = null;ByteBuffer dist = null;try {AssetFileDescriptor fd = mContext.getAssets().openFd(assetPath);fis = fd.createInputStream();dist = ByteBuffer.allocate((int) fd.getLength());src = Channels.newChannel(fis);src.read(dist);} catch (IOException e) {e.printStackTrace();} finally {if (src != null) {try {src.close();} catch (IOException e) {e.printStackTrace();}}if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}if (dist != null) {return dist.rewind();}return null;}private Bitmap loadBitmapFromAsset(String assetPath) { // 从asset中加载bitmapInputStream inputStream = null;Bitmap bitmap = null;try {inputStream = mContext.getAssets().open(assetPath);bitmap = BitmapFactory.decodeStream(inputStream);} catch (IOException e) {e.printStackTrace();} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}return bitmap;}private Bitmap loadBitmapFromDrawable(int resourceId) { // 从drawable中加载bitmapBitmapFactory.Options options = new BitmapFactory.Options();options.inPremultiplied = true;Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), resourceId, options);return bitmap;}private ByteBuffer getByteBuffer(float[] values) { // float数组转换为ByteBufferByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Float.BYTES);byteBuffer.order(ByteOrder.nativeOrder());for (int i = 0; i < values.length; i++) {byteBuffer.putFloat(values[i]);}byteBuffer.flip();return byteBuffer;}private ByteBuffer getByteBuffer(short[] values) { // short数组转换为ByteBufferByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Short.BYTES);byteBuffer.order(ByteOrder.nativeOrder());for (int i = 0; i < values.length; i++) {byteBuffer.putShort(values[i]);}byteBuffer.flip();return byteBuffer;}private ByteBuffer getByteBuffer(VertexPosCol[] values) { // VertexPosCol数组转换为ByteBufferByteBuffer byteBuffer = ByteBuffer.allocate(values.length * VertexPosCol.BYTES);byteBuffer.order(ByteOrder.nativeOrder());for (int i = 0; i < values.length; i++) {values[i].put(byteBuffer);}byteBuffer.flip();return byteBuffer;}private ByteBuffer getByteBuffer(VertexPosUV[] values) { // VertexPosUV数组转换为ByteBufferByteBuffer byteBuffer = ByteBuffer.allocate(values.length * VertexPosUV.BYTES);byteBuffer.order(ByteOrder.nativeOrder());for (int i = 0; i < values.length; i++) {values[i].put(byteBuffer);}byteBuffer.flip();return byteBuffer;}/** 顶点数据(位置+颜色)* 包含顶点位置和颜色*/public static class VertexPosCol {public static int BYTES = 16;public float x;public float y;public float z;public int color;public VertexPosCol() {}public VertexPosCol(float x, float y, float z, int color) {this.x = x;this.y = y;this.z = z;this.color = color;}public ByteBuffer put(ByteBuffer buffer) { // VertexPosCol转换为ByteBufferbuffer.putFloat(x);buffer.putFloat(y);buffer.putFloat(z);buffer.putInt(color);return buffer;}}/** 顶点数据(位置+纹理坐标)* 包含顶点位置和纹理坐标*/public static class VertexPosUV {public static int BYTES = 20;public float x;public float y;public float z;public float u;public float v;public VertexPosUV() {}public VertexPosUV(float x, float y, float z, float u, float v) {this.x = x;this.y = y;this.z = z;this.u = u;this.v = v;}public ByteBuffer put(ByteBuffer buffer) { // VertexPosUV转换为ByteBufferbuffer.putFloat(x);buffer.putFloat(y);buffer.putFloat(z);buffer.putFloat(u);buffer.putFloat(v);return buffer;}}
}

2.2 纹理贴图实现

        MainActivity.java

package com.zhyan8.texture;import android.os.Bundle;import androidx.appcompat.app.AppCompatActivity;import com.zhyan8.texture.filament.FLSurfaceView;public class MainActivity extends AppCompatActivity {private FLSurfaceView mFLSurfaceView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mFLSurfaceView = new MyFLSurfaceView(this);setContentView(mFLSurfaceView);mFLSurfaceView.init();mFLSurfaceView.setRenderMode(FLSurfaceView.RENDERMODE_CONTINUOUSLY);}@Overridepublic void onResume() {super.onResume();mFLSurfaceView.onResume();}@Overridepublic void onPause() {super.onPause();mFLSurfaceView.onPause();}@Overridepublic void onDestroy() {super.onDestroy();mFLSurfaceView.onDestroy();}
}

        MyFLSurfaceView.java

package com.zhyan8.texture;import android.content.Context;import com.google.android.filament.Camera;
import com.zhyan8.texture.filament.BaseModel;
import com.zhyan8.texture.filament.FLSurfaceView;public class MyFLSurfaceView extends FLSurfaceView {private BaseModel mMyModel;public MyFLSurfaceView(Context context) {super(context);}public void init() {mSkyboxColor = new float[] {0.965f, 0.941f, 0.887f, 1};super.init();}@Overridepublic void onDestroy() {mMyModel.destroy();super.onDestroy();}@Overrideprotected void setupScene() { // 设置Scene参数mMyModel = new Square(getContext(), mEngine);mScene.addEntity(mMyModel.getRenderable());}@Overrideprotected void onResized(int width, int height) {mCamera.setProjection(Camera.Projection.ORTHO, -1, 1, -1, 1, 0, 10);}
}

        Square.java

package com.zhyan8.texture;import android.content.Context;import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.RenderableManager.PrimitiveType;
import com.google.android.filament.TextureSampler;
import com.zhyan8.texture.filament.BaseModel;public class Square extends BaseModel {private String materialPath = "materials/square.filamat";private String texturePath = "textures/1.jpg";private VertexPosUV[] mVertices = new VertexPosUV[] {new VertexPosUV(-1f, -1f, 0f, 0f, 0f), // 左下new VertexPosUV(1f, -1f, 0f, 1f, 0f), // 右下new VertexPosUV(1f, 1f, 0f, 1f, 1f), // 右上new VertexPosUV(-1f, 1f, 0f, 0f, 1f) // 左上};private short[] mIndex = new short[] {0, 1, 2, 0, 2, 3};public Square(Context context, Engine engine) {super(context, engine);init();}private void init() {mBox = new Box(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.01f);mMaterial = loadMaterial(materialPath);mMaterialInstance.setParameter("mainTex", getTexture(texturePath), new TextureSampler());mVertexBuffer = getVertexBuffer(mVertices);mIndexBuffer = getIndexBuffer(mIndex);mRenderable = getRenderable(PrimitiveType.TRIANGLES, mIndex.length);}
}

        square.mat

material {name : square,shadingModel : unlit, // 禁用所有lighting// 自定义变量参数parameters : [{type : sampler2d,name : mainTex}],// 顶点着色器入参MaterialVertexInputs中需要的顶点属性requires : [uv0]
}fragment {void material(inout MaterialInputs material) {prepareMaterial(material); // 在方法返回前必须回调该函数material.baseColor = texture(materialParams_mainTex, getUV0());}
}

        transform.bat

@echo off
setlocal enabledelayedexpansion
set "srcFolder=../src/main/materials"
set "distFolder=../src/main/assets/materials"for %%f in ("%srcFolder%\*.mat") do (set "matfile=%%~nf"matc -p mobile -a opengl -o "!matfile!.filamat" "%%f"move "!matfile!.filamat" "%distFolder%\!matfile!.filamat"
)echo Processing complete.
pause

        说明:需要将 matc.exe 文件与 transform.bat 文件放在同一个目录下面,matc.exe 源自Filament环境搭建中编译生成的 exe 文件。双击 transform.bat 文件,会自动将 /src/main/materials/ 下面的所有 mat 文件全部转换为 filamat 文件,并移到 /src/main/assets/materials/ 目录下面。

        运行效果如下。

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

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

相关文章

c++可视化界面_新基建的福音:智慧楼宇可视化监控系统引领智能化新时代

前言智慧楼宇和人们的生活息息相关&#xff0c;楼宇智能化程度的提高&#xff0c;会极大程度的改善人们的生活品质&#xff0c;在当前 工业互联网 大背景下受到很大关注。目前 智慧楼宇可视化监控 的主要优点包括:智慧化 -- 智慧楼宇是一个生态系统&#xff0c;像人一样拥有感知…

table 多行 宽度不一致_“table”除了桌子,你还知道一些别的意思吗?

就比如"nine-nine table"这可是小学一年级必须要掌握的知识实际上&#xff0c;nine-nine table是九九乘法表&#xff0c;乘法表可以用times table表示&#xff0c;书面用语是multiplication table。table除了桌子的意思外&#xff0c;还有一些别的意思和表达&#xf…

ddos攻击工具_简单有效的ddos攻击防御方法

做过网站的站长大多有被ddos攻击的经历&#xff0c;不少人面对竞争对手的网站就是直接雇人ddos攻击网站&#xff0c;导致对方网站长期打不开&#xff0c;最后无奈关闭网站&#xff0c;初尧今天就告诉大家一个最简单也是最有效的防御方法。高防服务器/高防IP对于游戏&#xff0c…

回调函数中有回调函数吗_嗨,那里有回调!

回调函数中有回调函数吗因为是我的书包&#xff0c;所以我喜欢JavaScript 。 实际上&#xff0c;我已经开始喜欢JavaScritp的面向异步回调的编程风格 。 因此&#xff0c;当我发现自己处于非JavaScript环境中时&#xff08;例如Java&#xff09; &#xff0c;我往往会错过使用回…

python的django_django能用来做什么

Django是什么 Django是一个开放源代码的Web应用框架&#xff0c;由Python写成。采用了MT‘V的框架模式&#xff0c;即模型M&#xff0c;模板T和视图V。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的&#xff0c;即是CMS&#xff08;内容管理系统&am…

datatable如何生成级联数据_如何把Excel表数据批量生成条形码

条形码属于一维条码&#xff0c;是将宽度不等的多个黑条和空白&#xff0c;按照一定的编码规则排列&#xff0c;用以表达一组信息的图形标识符&#xff0c;条形码的种类比较多&#xff0c;比如常用的Code128码&#xff0c;Code39码&#xff0c;Code93码&#xff0c;EAN-13码&am…

android数字累加,Android自己设置View之数字自动增长

第一次写文&#xff0c;请多指教&#xff0c;有何问题及改进建议都可以告诉我-.-Idea来自金山词霸App的单词计数&#xff0c;下面先放图autoNumber.gif如上图&#xff0c;就是&#xff0c;下面开始进入自己设置View自己设置View步骤1. 自己设置属性2. 生成构造方法3. onMeasure…

settimeout怎么用_怎么实现一个3d翻书效果

本篇主要讨论以下两种翻书动画的实现&#xff1a;第一种是整页翻转的效果&#xff1a;这种整页翻转的效果主要是做rotateY的动画&#xff0c;并结合一些CSS的3d属性实现。第二种折线翻转的效果&#xff0c;如下图所示&#xff1a;主要是通过计算页面翻折过来的位置。这两种原理…

5个令人震惊的统计数据证明日志不足

事实证明&#xff0c;我们都犯有记录不当行为的罪行。 不相信我们吗&#xff1f; 这些统计数据可能会改变您的想法 当人们提出带有明显答案的问题时&#xff0c;这非常令人不快&#xff0c;因此&#xff0c;我不会坐在这里问您和您的团队是否使用日志文件来监视预生产和生产环…

linearregression_机器学习-TensorFlow建模过程 Linear Regression线性拟合应用

TensorFlow是咱们机器学习领域非常常用的一个组件&#xff0c;它在数据处理&#xff0c;模型建立&#xff0c;模型验证等等关于机器学习方面的领域都有很好的表现&#xff0c;前面的一节我已经简单介绍了一下TensorFlow里面基础的数据结构即&#xff1a;Tensor和Dataset&#x…

html页面调用存储过程,用WebBrowser实现HTML界面的应用

HTML的界面有以下特点&#xff1a;图文混排&#xff0c;格式灵活&#xff0c;可以包含Flash、声音和视频等&#xff0c;实现图文声像的多媒体界面&#xff0c;而且易于建立和维护。另外&#xff0c;HTML的显示环境一般机器上都具备&#xff0c;通常不需要安装额外的软件。当然&…

凡事多找找自己的原因_布袋除尘器灰斗积粉过多、堵灰该咋办?别急,从这8个方面找原因...

灰斗布置在袋室的下部&#xff0c;它除了存放收集下来的粉尘以外&#xff0c;还作为下进气总管使用&#xff0c;当含尘气体进入袋室前先进入灰斗&#xff0c;由于灰斗内容积较大&#xff0c;使得气流速度降低&#xff0c;加之气流方向的改变&#xff0c;使得较粗的尘粒在这里得…

python怎么改变字体大小_Python-docx 整体修改或者部分修改文字的大小和字体类型...

Python中可以用docx来生成word文档&#xff0c;docx中可以自定义文字的大小和字体等。 其中要整体修改文字的字体大小和字体&#xff0c;可以用以下方法&#xff1a; newfile docx.Document() newfile.styles[Normal].font.name Times New Roman newfile.styles[Normal]._ele…

cad设计院常用字体_如何把CAD图纸坐标转换成现场坐标?

使用CAD软件画建筑施工图是很常见的&#xff0c;特别是在施工现场&#xff0c;为了不受现场场地落差的影响&#xff0c;需要使用全站仪&#xff0c;但是使用全站仪需要把CAD图纸转换成CAD坐标&#xff0c;很多人可能并不知道该怎么转换&#xff0c;下面我们就来介绍一下如何把图…

sqlserver select 数值精度_SQL Server读懂语句运行 (二) SET STATISTICS IO ON

对于语句的运行&#xff0c;除了执行计划本身&#xff0c;还有一些其他因素要考虑&#xff0c;例如语句的编译时间、执行时间、做了多少次磁盘读等。这些信息对分析问题很有价值。1 SET STATISTICS TIME ON 2 SET STATISTICS IO ON 3 SET STATISTICS PROFILE ON今天&#xff0c…

.net webservice studio调用方法传参_springboot整合WebService简单版

一.什么是webservice这里给大家分享一下我们的专栏《Java 进阶集中营》&#xff0c;每天都会给大家分享一个最新的java技术内容&#xff0c;有优秀的技术讯息&#xff0c;也欢迎分享在我的专栏里。JAVA 进阶集中营​zhuanlan.zhihu.com二.springboot整合webservice 整合webserv…

如何用html5编写彩色同心圆,HTML5 canvas 同心圆动画

原创。产生的动画效果&#xff1a;* 生成文字渐变颜色随时间的变化。* 使得一组同心圆的取色&#xff0c;随时间而变化1.[图片] open_source.png2.[代码][JavaScript]代码var cnew Array("red","blue","cyan","darkGray","green&…

swiper如何防止冲突_冲突管理:化冲突为机会的8个谈话技巧,从此告别争吵和冷战...

书语人间&#xff1a;每天10分钟&#xff0c;读懂1本好书&#xff0c;点击文章右边的「关注」&#xff0c;一起成长大家好呀~今天&#xff0c;灵遥将继续为你带来《解决冲突的关键技巧&#xff1a;如何增加你的有效社交》一书的共读。上一篇里&#xff0c;我们读到了让聆听和谈…

spss非线性回归分析步骤_SPSS与简单线性回归分析

对数据进行简单线性回归分析常按照以下步骤&#xff1a;1根据研究目的确定因变量和自变量现研究某服装店销售额和客流量的关系&#xff0c;销售额为因变量&#xff0c;客流量为自变量&#xff0c;共计36条数据。2 判断有无异常值判断方法&#xff1a;⑴通过绘制散点图直观观察&…

通过OAuth 2.0和Okta构建具有安全的服务器到服务器通信的Spring Boot应用

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕&#xff1f; 尝试使用Okta API进行托管身份验证&#xff0c;授权和多因素身份验证。 大多数OAuth 2.0指南都围绕用户的上下文&#xff0c;即使用Google&#xff0c;Github…